Java对象真的会在栈上分配内存?

JVM中高深的优化技术,如同类继承关系分析,该技术并非直接去优化代码,而是一种为其他优化措施提供依据的分析技术。

分析对象的动态作用域,当某对象在方法里被定义后,它可能

  • 方法逃逸

被外部方法引用,例如作为参数传递给其他方法

  • 线程逃逸

被外部线程访问,例如赋值给可以在其他线程中访问的实例变量

所以 Java 对象由低到高的逃逸程度即为:

  • 不逃逸 =》
  • 方法逃逸 =》
  • 线程逃逸

若能确定一个对象

  • 不会逃逸到方法或线程外(即其它方法、线程无法访问到该对象)
  • 或逃逸程度较低(只逃逸出方法而不逃逸出线程)

则可为该对象实例采取不同程度的优化方案。

 优化方案

 栈上分配(Stack Allocations)

由于复杂度等原因,HotSpot中目前暂时还没有做这项优化,但一些其他的虚拟机(如Excelsior JET)使用了该优化。

JVM的GC模块会回收堆中不再使用的对象,但如下回收动作

  • 标记筛选出可回收对象
  • 回收和整理内存

都需耗费大量资源。

若确定一个对象不会逃逸出线程,那让该对象在栈上分配内存就是个不错主意,对象所占用内存空间就可随栈帧出栈而销毁。

在一般应用中,完全不会逃逸的局部对象和不会逃逸出线程的对象所占比例很大,若能使用栈上分配,则大量对象就会随方法结束而自动销毁,GC系统压力会下降很多。

栈上分配可支持方法逃逸,但不能支持线程逃逸。

 标量替换(Scalar Replacement)

 标量

若一个数据已经无法再分解成更小数据,JVM中的原始数据类型(如 int、long 等数值类型及 reference 类型)都不能再进一步分解,这些数据即为标量。

 聚合量

若一个数据可继续分解,则称为聚合量(Aggregate),比如 Java 对象就是聚合量。

 标量替换

把一个Java对象拆散,根据程序访问情况,将其用到的成员变量恢复为原始类型来访问。

假如逃逸分析能证明一个对象不会被方法外部访问,并且该对象可被分解,那么程序真正执行时将可能不去创建该对象,而改为直接创建它的若干个被这方法使用的成员变量。

将对象拆分后:

  • 可让对象的成员变量在栈上 (栈上存储的数据,很大概率会被JVM分配至物理机器的高速寄存器中存储)分配和读写
  • 为后续进步优化创建条件
【声明】:芜湖站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

相关文章