深入理解jvm逃逸分析

简介: 深入理解jvm逃逸分析

jvm里面的堆会分配指定的内存空间用来存储对象信息,但是当对象信息过多的时候,GC进行垃圾回收时,过多的对象需要进行回收,会导致效率的底下。


因此了解常见的jvm优化技巧也就很有必要了。


1.如何理解逃逸分析?


所谓的逃逸分析是指方法创建对象之后,除了在方法体内被引用到之外,还在别处也被引用到了。由于GC进行对象回收的时候需要判断该对象是否有被引用,因此当相应方法执行完毕后,由于方法类对象还被外部程序引用,就会导致相应对象无法被GC回收处理,也就造成了内存逃逸现象。


2.什么是栈上分配?


在以往的java程序运行时候,对象的内存空间都是通过堆来进行分配的。方法体内的局部变量都是通过栈来进行内存空间分配的,因此如果我们能够控制一个对象的活动范围只在一个局部方法里面的话,并且控制该对象的空间分配就存在于栈里面。这样随着栈的出栈操作,对象就会被销毁,减少了对于GC的效率影响。


3.同步消除是什么?


当通过逃逸分析之后,发下某个对象在没有被外部线程所引用的时候,他的读写竞争也就不存在了,这个时候就可以消除他的同步操作。


4.标量替换


标量是指java虚拟机里面的原始数据(那些不可再去细化分隔的基本类型,例如int,float这些)。当某个数据可以被继续进行细化拆分的时候,我们称之为聚合量(例如我们常说的对象)。如果jvm的逃逸分析分析出来了某个对象不会被外部所引用。那么当jvm执行相应函数的时候也不会直接创建相应的对象,而是通过将对象里面的各个属性拆开单独创建,单独维护。这样做的好处在于可以将原本需要连续空间存储的对象给拆分开来,减少连续内存空间的占用。


由于java的对象内存分配大多数情况下都是通过堆来进行分配,因此它的垃圾回收效率成为了java性能较慢的一个因素,这也是java语言被人吐槽的一个缺点。


5.逃逸分析总结


逃逸分析的原理理解起来很简单,但JVM在应用过程中,还是有诸多考虑。


比如,逃逸分析不能在静态编译时进行,必须在JIT里完成。原因是,与java的动态性有冲突。因为你可以在运行时,通过动态代理改变一个类的行为,此时,逃逸分析是无法得知类已经变化了。逃逸分析另一个重要的优化 - 同步消除。如果你定义的类的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。


接下来,我将会通过一段实操记录来演示逃逸分析过程中的堆栈分配情况。


jdk环境:1.8

操作系统 :win10


这段程序代码在编译器里面跑完之后,再去输入相应的jmap指令查看堆栈内存分配的时候可能报出异常,小编我谷歌了很多内容都没法解决,于是这次实验采用最原始的cmd命令来进行:


首先是java程序代码:


public class Test {
    static class Admin{
        public String name;
    }
    public static void testEscape(){
        Admin admin=new Admin();
    }
    public static void main(String[] args) {
        System.out.println("start");
        long begin=System.currentTimeMillis();
        for(long i=0;i<1000000;i++){
            testEscape();
        }
        long end=System.currentTimeMillis();
        System.out.println("time:"+(end-begin));
        try {
            Thread.sleep(10000000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
}
复制代码


然后通过javac命令来将程序编译成class字节码,再通过java命令来执行程序。


网络异常,图片无法展示
|


这个时候程序中的线程处于睡眠状态,进入cmd窗口中,通过jps和jmap来查看相应的内存分配情况。


网络异常,图片无法展示
|


由于小编使用的是jdk8,因此当前环境中的逃逸分析已经开启了。所以这个时候会发现,堆里面的Admin对象一共创建了90613个。那么如果我们将逃逸分析关闭之后,实际的一个内存分配又将会是如何的了?


通过查阅相应的jvm指令之后,我们可以看到相应的关闭逃逸分析指令为:


-XX:-DoEscapeAnalysis


那么好,我们接着来继续之前的问题深究。


网络异常,图片无法展示
|


这一次,小编将逃逸分析关闭之后,再去通过jps,jmap命令查看内存分配信息。


网络异常,图片无法展示
|


这一次你会发现,堆里面创建的Admin对象为1000000个,和之前开启逃逸分析时创建的90613个相差甚远。


同样,通过是否开启逃逸分析会发现,前者的消耗时间比后者要短,也就是说开启了逃逸分析之后,会有部分对象在栈上边直接分配空间,这样GC回收的压力也就减少了,所以速率也会有所提升。


模仿着上面的操作,也可以试试标量替换指令


网络异常,图片无法展示
|


通过实验验证,相应的性能都会受到影响。而且这些指令生效的前提是开启了逃逸分析。


同样通过开启和关闭相应的锁指令也会影响相应的性能。


加锁之后的逃逸分析代码案例:


public class Test {


public static void lock(){
    int[] numbers=new int[65];
    synchronized (numbers) {
        numbers[0]=1;
    }
}
public static void main(String[] args)   {
    long begin=System.currentTimeMillis();
    for(int i=0;i<100000000;i++){
        lock();
    }
    long end=System.currentTimeMillis();
    System.out.println(end-begin);
}
复制代码


}

同样,通过相应的锁指令执行程序


java -XX:+DoEscapeAnalysis -XX:+EliminateLocks -XX:+PrintGC Test


网络异常,图片无法展示
|


java -XX:+DoEscapeAnalysis -XX:-EliminateLocks -XX:+PrintGC Test


网络异常,图片无法展示
|


常见的jvm逃逸分析的指令:


-XX:+EliminateLocks开启锁消除(jdk1.8默认开启)

-XX:-EliminateLocks 关闭锁消除

-XX:+EliminateAllocations开启标量替换(jdk1.8默认开启)

-XX:-EliminateAllocations 关闭标量替换

-XX:+DoEscapeAnalysis开启逃逸分析(jdk1.8默认开启)

-XX:-DoEscapeAnalysis 关闭逃逸分析


通过上述的这些实验,我们可以发现,开启了逃逸分析之后,java语言中的对象在创建的时候并非全都被分配到了堆里面。其实在编译期间,JIT会对程序代码做出很多的优化,主要的核心目的就在于减少对于内存堆分配压力,其中逃逸分析就是一种技术。JIT编译器通过逃逸分析的结果来判断是否要将该对象的内存分配到栈上。因此程序的运行效率也会有所提升。


但是逃逸分析这项技术早在上世纪就已经有论文提出了,却是到了jdk1.6版本之后才渐渐冒出水面,当然逃逸分析也是有它的弊端的。由于每次进行逃逸分析的时候都是需要进行消耗一定的性能,如果相应程序里面所有的对象都是出于逃逸状态,那么逃逸分析也就变得失去了意义,反而还降低了性能。

目录
相关文章
|
1月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
41 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
5月前
|
缓存 Java
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
50 0
|
1月前
|
存储 Java PHP
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
58 0
|
4月前
|
Java
jmap 查看jvm内存大小并进行dump文件内存分析
jmap 查看jvm内存大小并进行dump文件内存分析
94 3
|
4月前
|
运维 监控 Java
(十)JVM成神路之线上故障排查、性能监控工具分析及各线上问题排错实战
经过前述九章的JVM知识学习后,咱们对于JVM的整体知识体系已经有了全面的认知。但前面的章节中,更多的是停留在理论上进行阐述,而本章节中则更多的会分析JVM的实战操作。
107 1
|
4月前
|
Arthas 监控 Java
JVM内存问题之使用gperftools分析JNI Memory泄漏的具体步骤是什么
JVM内存问题之使用gperftools分析JNI Memory泄漏的具体步骤是什么
104 2
|
3月前
|
监控 JavaScript Java
JVM源码级别分析G1发生FullGC元凶的是什么
线上系统遭遇频繁Old GC问题,监控显示出现多次“to-space exhausted”日志,这表明垃圾回收过程中因年轻代 Survivor 区或老年代空间不足导致对象晋升失败。通过 JVM 源码分析,此问题源于对象转移至老年代失败时,JVM 无法找到足够的空间存放存活对象。进一步排查发现大对象分配占用了预留空间,加剧了空间不足的情况。使用 JFR 分析工具定位到定期报表序列化导致大量大对象生成,通过改用堆外内存进行序列化输出,最终解决了频繁 Old GC 问题。
117 0
|
4月前
|
监控 Java 开发者
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
202 2
|
4月前
|
人工智能 Java
JVM内存问题之当老年代缓慢增加且Full GC无法清除时,应如何使用MAT进行分析
JVM内存问题之当老年代缓慢增加且Full GC无法清除时,应如何使用MAT进行分析
198 0
|
4月前
|
监控 算法 Java
怎么用JDK自带工具进行JVM内存分析
JVM内存分析工具,如`jps`、`jcmd`、`jstat`、`jstack`和`jmap`,是诊断和优化Java应用的关键工具。`jps`列出Java进程,`jcmd`执行诊断任务,如查看JVM参数和线程堆栈,`jstat`监控内存和GC,`jstack`生成线程堆栈信息,而`jmap`则用于生成堆转储文件。这些工具帮助排查内存泄漏、优化内存配置、性能调优和异常分析。例如,`jmap -dump:file=heapdump.hprof &lt;PID&gt;`生成堆转储文件,之后可以用Eclipse Memory Analyzer (MAT)等工具分析。