java逃逸分析

概念

  • 对象逃逸分析:是一种有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java虚拟机能够分析出一个新的对象的引用范围从而决定是否要将这个对象分配到堆上。Java1.7后默认开启逃逸分析的选项。Java的JIT编译器,能够在方法重载或者动态加载代码的时候对代码进行逃逸分析,同时Java对象在堆上分配和内置线程的特点使得逃逸分析成Java的重要功能。
  • JIT技术 :为了解决JVM执行字节码速度问题。引入JIT(即时编译)技术,当JVM发现某个方法或代码块运行的特别频繁的时候,就会认为这是"热点代码" ,然后JIT会把部分热点代码翻译成本地机器相关的机器码,然后再把翻译后的机器码缓存起来,以备下次使用。

对象逃逸状态

基于逃逸分析,一个对象可以被三种逃逸状态标记。

  1. 全局级别逃逸状态

一个对象能从一个方法或者当前线程中逃逸,那么会被标记为全局逃逸状态

  1. 参数级别逃逸

如果一个对象被作为参数传递给一个方法,但是在这个方法之外无法访问或者对其他线程不可见,这个对象标记为参数级别逃逸。

  1. 不逃逸

一个对象不会产生逃逸。

逃逸分析的作用

通过逃逸分析,在不存在逃逸下JVM可以进行以下优化。

  • 同步消除

线程同步本身比较耗时,如果确定一个变量不会逃逸出线程,无法被其他线程访问到,那么这个变量的读写就不会存在竞争,对这个变量的同步措施可以清除。

  • 将堆分配转为栈上分配

在一般应用中,不会逃逸的局部对象占比很大,如果使用栈上分配,那大量对象会随着方法结束而自动销毁,减轻垃圾回收系统压力。

  • 分离对象或标量替换

标量就是不可分割的量,java中基本数据类型,reference类型都是标量。相对的一个数据可以继续分解,它就是聚合量。如果把一个对象拆散,将其成员变量恢复到基本类型来访问就叫做标量替换。如果逃逸分析证明一个对象不会被外部访问,并且这个对象可以被拆散的话,那么程序真正执行的时候可能在栈上创建若干个成员变量。

验证逃逸分析对对象分配的影响

验证代码如下:

java 复制代码
public static void main(String[] args) {
    long a1 = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        alloc();
    }
    // 查看执行时间
    long a2 = System.currentTimeMillis();
    System.out.println("cost " + (a2 - a1) + " ms");
    // 为了方便查看堆内存中对象个数,线程sleep
    try {
        Thread.sleep(100000);
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }
}

private static void alloc() {
    User user = new User();
}

static class User {

}

关闭逃逸分析

  • 配置JVM参数

-Xmx4G -Xms4G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

-通过jmap命令分析堆对象

powershell 复制代码
➜  ~ jps
2809 StackAllocTest
2810 Jps
➜  ~ jmap -histo 2809

 num     #instances         #bytes  class name
----------------------------------------------
   1:           524       87282184  [I
   2:       1000000       16000000  StackAllocTest$User
   3:          6806        2093136  [B
   4:          8006        1320872  [C
   5:          4188         100512  java.lang.String
   6:           581          66304  java.lang.Class

从上面的jmap执行结果中我们可以看到,堆中共创建了100万个StackAllocTest$User实例。

  • 结论
    关闭逃逸分析的情况下,对象分配在堆上。

开启逃逸分析

  • 配置JVM参数

Xmx4G -Xms4G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

  • 通过jmap命令分析堆对象
powershell 复制代码
➜  ~ jps
709
2858 Launcher
2859 StackAllocTest
2860 Jps
➜  ~ jmap -histo 2859

 num     #instances         #bytes  class name
----------------------------------------------
   1:           524      101944280  [I
   2:          6806        2093136  [B
   3:         83619        1337904  StackAllocTest$User
   4:          8006        1320872  [C
   5:          4188         100512  java.lang.String
   6:           581          66304  java.lang.Class

从以上打印结果中可以发现,开启了逃逸分析之后(-XX:+DoEscapeAnalysis),在堆内存中只有8万多个StackAllocTest$User对象。也就是说在经过JIT优化之后,堆内存中分配的对象数量,从100万降到了8万。

参考

java 逃逸问题 java 逃逸分析技术

相关推荐
专注_每天进步一点点1 分钟前
【java开发】写接口文档的札记
java·开发语言
代码方舟4 分钟前
Java企业级实战:对接天远名下车辆数量查询API构建自动化风控中台
java·大数据·开发语言·自动化
flysh056 分钟前
C# 中类型转换与模式匹配核心概念
开发语言·c#
AC赳赳老秦6 分钟前
Python 爬虫进阶:DeepSeek 优化反爬策略与动态数据解析逻辑
开发语言·hadoop·spring boot·爬虫·python·postgresql·deepseek
浩瀚之水_csdn7 分钟前
Python 三元运算符详解
开发语言·python
zgl_2005377913 分钟前
ZGLanguage 解析SQL数据血缘 之 标识提取SQL语句中的目标表
java·大数据·数据库·数据仓库·hadoop·sql·源代码管理
liwulin050614 分钟前
【JAVA】创建一个不需要依赖的websocket服务器接收音频文件
java·服务器·websocket
钦拆大仁20 分钟前
统一数据返回格式和统一异常处理
java
源代码•宸32 分钟前
GoLang八股(Go语言基础)
开发语言·后端·golang·map·defer·recover·panic
czlczl2002092533 分钟前
OAuth 2.0 解析:后端开发者视角的原理与流程讲解
java·spring boot·后端