作者:1157476701qq | 来源:互联网 | 2023-08-31 11:31
这期内容当中小编将会给大家带来有关如何理解JVM本地方法栈&堆,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
今天我们一起了解一下关于本地方法栈和Java堆的相关知识。
本地方法栈 本地方法栈与Java虚拟机栈发挥的作用相同,区别在于Java虚拟机栈是为虚拟机执行Java方法服务,而本地方法栈是为虚拟机执行Native方法服务。
Java堆 Java堆是虚拟机管理的最大的一块内存区域,堆主要用来存放对象的实例。Java堆主要分为:
Survivor(s0、s1也可以成为from、to,属于新生代) 在绝大数情况下,对象都会被分配在新生代,在经历过一次新生代回收后,如果对象还存活那么会进入s0或者s1,每经过一次新生代回收,如果对象存活那么他的年龄将会加,当对象的年龄到达一定的条件时会进入老年代。当然如果对象非常大,超出了新生代的大小,对象也有可能直接分配在老年代。
随着JIT编译器的发展以及逃逸分析技术的成熟,栈上分配、标量替换等技术使得对象分配在堆中变得不再那么绝对。
栈上分配 栈上分配是指对那些线程私有的对象可以将他们打散分散在栈上,分配在栈上的好处是随着函数调用结束对象自动销毁,不需要垃圾回收器的介入,从而提高系统的性能。
栈上分配的基础是要进行逃逸分析,逃逸分析的目的是判断对象的作用域是否可能逃出函数体。
逃逸分析只有在Server模式下才可以启动(这个作者由于本人Java环境是Server环境,没有做验证,如果有读者感兴趣,可以使用Client模式启动做一下验证然后截图留言)。参数是:
public class EscapeAnalysis {
public static class User {
private int id = 1;
private String name = "xiaoming";
}
public static void alloc() {
User u = new User();
u.id = 5;
u.name = "xiaohong";
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i <100000000; i++) {
alloc();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
执行以下命令将源码文件编译成字节码文件并且运行字节码
javac -d . EscapeAnalysis.javajava -server -Xmx10m -Xms10m -XX:-DoEscapeAnalysis -XX:+PrintGC jvm/heap/EscapeAnalysis
由于Java8默认开启了逃逸分析,所以我们用-XX:-DoEscapeAnalysis移除逃逸分析看一下效果。
通过上图我们发现由于对象分配在堆上,但我们指定的最大堆为10m,不够存储我们大量User对象,因此虚拟机需要不停的进行垃圾回收来释放堆空间,程序执行时间928ms。
下面我们通过-XX:-EliminateAllocations将标量替换关闭,也就是说不允许对象打散分配在栈上。
java -server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+PrintGC jvm/heap/EscapeAnalysis
当我们关闭标量替换以后,会发现对象依旧不能在栈上分配。
下面让我们看一下当我们打开逃逸分析和标量替换(允许Use对象的id和name当做局部变量在栈上存储时)执行效果:
java -server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+PrintGC jvm/heap/EscapeAnalysis
通过上图可以看出,我们的程序只进行了一次垃圾回收,总运行时间5ms,相比之前接近1s中的运行时间性能翻了将近200倍,可见由于栈上分配的优化,使得小对象可以直接分配在栈上,避免了垃圾回收器的介入,大幅度提高了我们程序的性能。
栈上分配需要同时开启逃逸分析和标量替换,Java8 Server模式下这两个参数都是默认开启的。
上述就是小编为大家分享的如何理解JVM本地方法栈&堆了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程笔记行业资讯频道。