前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JVM本地方法栈&堆

JVM本地方法栈&堆

作者头像
shysh95
发布2020-07-16 15:57:58
6010
发布2020-07-16 15:57:58
举报
文章被收录于专栏:shysh95

上一节我们介绍了程序计数器和Java虚拟机栈,今天我们一起了解一下关于本地方法栈和Java堆的相关知识。

本地方法栈

本地方法栈与Java虚拟机栈发挥的作用相同,区别在于Java虚拟机栈是为虚拟机执行Java方法服务,而本地方法栈是为虚拟机执行Native方法服务。

Java堆

Java堆是虚拟机管理的最大的一块内存区域,堆主要用来存放对象的实例。Java堆主要分为:

  • 新生代(Eden)
  • 老年代
  • Survivor(s0、s1也可以成为from、to,属于新生代)

在绝大数情况下,对象都会被分配在新生代,在经历过一次新生代回收后,如果对象还存活那么会进入s0或者s1,每经过一次新生代回收,如果对象存活那么他的年龄将会加,当对象的年龄到达一定的条件时会进入老年代。当然如果对象非常大,超出了新生代的大小,对象也有可能直接分配在老年代。

随着JIT编译器的发展以及逃逸分析技术的成熟,栈上分配、标量替换等技术使得对象分配在堆中变得不再那么绝对。

栈上分配

栈上分配是指对那些线程私有的对象可以将他们打散分散在栈上,分配在栈上的好处是随着函数调用结束对象自动销毁,不需要垃圾回收器的介入,从而提高系统的性能。

栈上分配的基础是要进行逃逸分析,逃逸分析的目的是判断对象的作用域是否可能逃出函数体。

逃逸分析只有在Server模式下才可以启动(这个作者由于本人Java环境是Server环境,没有做验证,如果有读者感兴趣,可以使用Client模式启动做一下验证然后截图留言)。参数是:

  • -XX:+DoEscapeAnalysis
代码语言:javascript
复制
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);
    }
}

执行以下命令将源码文件编译成字节码文件并且运行字节码

代码语言:javascript
复制
javac -d . EscapeAnalysis.java
java -server -Xmx10m -Xms10m -XX:-DoEscapeAnalysis -XX:+PrintGC jvm/heap/EscapeAnalysis

由于Java8默认开启了逃逸分析,所以我们用-XX:-DoEscapeAnalysis移除逃逸分析看一下效果。

通过上图我们发现由于对象分配在堆上,但我们指定的最大堆为10m,不够存储我们大量User对象,因此虚拟机需要不停的进行垃圾回收来释放堆空间,程序执行时间928ms。

下面我们通过-XX:-EliminateAllocations将标量替换关闭,也就是说不允许对象打散分配在栈上。

代码语言:javascript
复制
java -server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+PrintGC jvm/heap/EscapeAnalysis 

当我们关闭标量替换以后,会发现对象依旧不能在栈上分配。

下面让我们看一下当我们打开逃逸分析和标量替换(允许Use对象的id和name当做局部变量在栈上存储时)执行效果:

代码语言:javascript
复制
java -server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+PrintGC jvm/heap/EscapeAnalysis 

通过上图可以看出,我们的程序只进行了一次垃圾回收,总运行时间5ms,相比之前接近1s中的运行时间性能翻了将近200倍,可见由于栈上分配的优化,使得小对象可以直接分配在栈上,避免了垃圾回收器的介入,大幅度提高了我们程序的性能。

栈上分配需要同时开启逃逸分析和标量替换,Java8 Server模式下这两个参数都是默认开启的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员修炼笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本地方法栈
  • Java堆
    • 栈上分配
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档