前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【原创】JVM系列04 | 栈上分配

【原创】JVM系列04 | 栈上分配

作者头像
java进阶架构师
发布2020-05-29 17:23:32
4260
发布2020-05-29 17:23:32
举报
文章被收录于专栏:Java进阶架构师Java进阶架构师

本文是何适 JVM 修仙系列第 4 篇,文末有本系列文章汇总。

当面试官问你对象都分配哪里,你把 JVM 内存结构介绍一下然后说分配在堆上,没啥问题,给你打 8 分。如果你还能聊一聊栈上分配,一定是加分项,我想面试官会考虑给你 10 分。

1. 栈上分配理论

是什么

将线程私有的不可能被其他线程访问的对象打散分配在栈上,而不是分配在堆上。打散分配意思是将对象的不同属性分别分配给不同的局部变量。

好处

  1. 栈上分配速度快。
  2. 对象销毁不需要垃圾回收,因为方法执行结束后局部变量就销毁了。

缺点

栈空间较小,大对象不适合在栈上分配。

2. 逃逸分析

如何判断对象是线程私有的呢,就要通过逃逸分析。逃逸指的是逃出当前线程,所以逃逸对象就是可以被其他线程访问的对象,非逃逸对象就是线程私有的对象。

举例:逃逸对象和非逃逸对象

代码语言:javascript
复制
public class StackTest {
    public static User user1;

    public static void runAway1() {
        user1 = new User();// 逃逸对象
    }

    public static void runAway2() {
        User user2 = new User();// 非逃逸对象
    }
}

3. 栈上分配举例

举例:非逃逸对象栈上分配

代码语言:javascript
复制
public class StackTest {
    public static User user1;

    public static void runAway1() {
        user1 = new User();// 逃逸对象
        user1.id = 1;
        user1.name = "user";
    }

    public static void runAway2() {
        User user2 = new User();// 非逃逸对象
        user2.id = 2;
        user2.name = "user";
    }

    public static void main(String[] args) {
        for (int i = 0; i <= 99999999; i++) {
            // runAway1();
            runAway2();
        }
    }
}

class User {
    int id;
    String name;
}

执行代码时设置如下参数:

代码语言:javascript
复制
-server -Xss128K -Xmx100m -Xms100m -XX:+PrintGC

-server:server模式下才能设置栈上分配
-Xss128K:栈最大内存
-Xmx100m:堆最大内存
-XX:+PrintGC:打印GC日志

代码中创建一个 User 对象需要 16 字节内存,循环 1 亿次,大概需要 1.5GB 空间。

循环执行runAway1();1 亿次,堆内存 100M 小于 1.5G,所以会发生 GC,打印很多 GC 日志。

代码语言:javascript
复制
[GC (Allocation Failure)  25600K->800K(98304K), 0.0009909 secs]
[GC (Allocation Failure)  26400K->832K(98304K), 0.0006649 secs]
[GC (Allocation Failure)  26432K->776K(98304K), 0.0006009 secs]
[GC (Allocation Failure)  26376K->816K(98304K), 0.0009932 secs]
[GC (Allocation Failure)  26416K->696K(98304K), 0.0009381 secs]
...

循环执行runAway2();1 亿次,由于对象 user2 是线程私有的逃逸对象,执行一次 runAway2()创建一个 User 对象,一次 runAway2()执行完随着局部变量销毁,user2 对象也就销毁了,所以下次执行 runAway2()再创建 user2 对象时还有原来大小的栈空间。循环执行,不会有内存不够的问题,当然也就不会打印出 GC 日志。

4. 总结

参考资料

  1. 《深入理解 Java 虚拟机(第 2 版) : JVM 高级特性与最佳实践》
  2. 《实战 Java 虚拟机 : JVM 故障诊断与性能优化》

JVM 系列文章汇总

【原创】JVM 系列 01 | 开篇 【原创】JVM 系列 02 | Java 虚拟机结构 【原创】JVM 系列 03 | Java 栈—方法是如何调用的?

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

本文分享自 java进阶架构师 微信公众号,前往查看

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

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 栈上分配理论
  • 2. 逃逸分析
  • 3. 栈上分配举例
  • 4. 总结
    • 参考资料
    • JVM 系列文章汇总
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
    http://www.vxiaotou.com