科百科
当前位置: 首页 范文大全

java对象存在堆里(Java中的对象都是在堆上分配的吗)

时间:2023-05-25 作者: 小编 阅读量: 1 栏目名: 范文大全

当一个变量(或对象)在子程序中被分配时,一个指向变量的指针可能逃逸到其它执行线程中,或是返回到调用者子程序。逃逸分析确定某个指针可以存储的所有地方,以及确定能否保证指针的生命周期只在当前进程或线程中。以下的例子说明了一种对象逃逸的可能性。这说明逃逸分析确实降低了堆内存的压力。所以,在对象不逃逸出作用域并且能够分解为纯标量表示时,对象就可以在栈上分配。



Java对象实例和数组元素都是在堆上分配内存的吗?

答:不一定。满足特定条件时,它们可以在(虚拟机)栈上分配内存。



JVM内存结构很重要,多多复习

这和我们平时的理解可能有些不同。虚拟机栈一般是用来存储基本数据类型、引用和返回地址的,怎么可以存储实例数据了呢?

这是因为Java JIT(just-in-time)编译器进行的两项优化,分别称作逃逸分析(escape analysis)和标量替换(scalar replacement)。



注意看一下JIT的位置

中文维基上对逃逸分析的描述基本准确,摘录如下:

在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。当一个变量(或对象)在子程序中被分配时,一个指向变量的指针可能逃逸到其它执行线程中,或是返回到调用者子程序。

如果一个子程序分配一个对象并返回一个该对象的指针,该对象可能在程序中被访问到的地方无法确定——这样指针就成功“逃逸”了。如果指针存储在全局变量或者其它数据结构中,因为全局变量是可以在当前子程序之外访问的,此时指针也发生了逃逸。

逃逸分析确定某个指针可以存储的所有地方,以及确定能否保证指针的生命周期只在当前进程或线程中。

简单来讲,JVM中的逃逸分析可以通过分析对象引用的使用范围(即动态作用域),来决定对象是否要在堆上分配内存,也可以做一些其他方面的优化。

关于逃逸分析,大家可以看下这篇文章:面试问我 Java 逃逸分析,瞬间被秒杀了。以下的例子说明了一种对象逃逸的可能性。

static StringBuilder getStringBuilder1(String a, String b) {

StringBuilder builder = new StringBuilder(a);

builder.append(b);

return builder; // builder通过方法返回值逃逸到外部

static String getStringBuilder2(String a, String b) {

StringBuilder builder = new StringBuilder(a);

builder.append(b);

return builder.toString(); // builder范围维持在方法内部,未逃逸

以JDK 1.8为例,可以通过设置JVM参数-XX: DoEscapeAnalysis、-XX:-DoEscapeAnalysis来开启或关闭逃逸分析(默认当然是开启的)。

下面先写一个没有对象逃逸的例子。

public class EscapeAnalysisTest {

public static void main(String[] args) throws Exception {

long start = System.currentTimeMillis();

for (int i = 0; i < 5000000; i) {

allocate();

}

System.out.println((System.currentTimeMillis() - start)" ms");

Thread.sleep(600000);

}

static void allocate() {

MyObject myObject = new MyObject(2019, 2019.0);

}

static class MyObject {

int a;

double b;

MyObject(int a, double b) {

this.a = a;

this.b = b;

}

}

然后通过开启和关闭DoEscapeAnalysis开关观察不同。

关闭逃逸分析

~ java -XX:-DoEscapeAnalysis EscapeAnalysisTest

76 ms

~ jmap -histo 26031

num #instances #bytes class name

----------------------------------------------

1: 5000000 120000000 me.lmagics.EscapeAnalysisTest$MyObject

2: 636 12026792 [I

3: 3097 1524856 [B

4: 5088 759960 [C

5: 3067 73608 java.lang.String

6: 623 71016 java.lang.Class

7: 727 43248 [Ljava.lang.Object;

8: 532 17024 java.io.File

9: 225 14400 java.net.URL

10: 334 13360 java.lang.ref.Finalizer

# ......

开启逃逸分析

~ java -XX: DoEscapeAnalysis EscapeAnalysisTest

4 ms

~ jmap -histo 26655

num #instances #bytes class name

----------------------------------------------

1: 592 11273384 [I

2: 90871 2180904 me.lmagics.EscapeAnalysisTest$MyObject

3: 3097 1524856 [B

4: 5088 759952 [C

5: 3067 73608 java.lang.String

6: 623 71016 java.lang.Class

7: 727 43248 [Ljava.lang.Object;

8: 532 17024 java.io.File

9: 225 14400 java.net.URL

10: 334 13360 java.lang.ref.Finalizer

# ......

可见,关闭逃逸分析之后,堆上有5000000个MyObject实例,而开启逃逸分析之后,就只剩下90871个实例了,不管是实例数还是内存占用都只有原来的2%不到。

另外,如果把堆内存限制得小一点(比如加上-Xms10m -Xmx10m),并且打印GC日志(-XX: PrintGCDetails)的话,关闭逃逸分析还会造成频繁的GC,开启逃逸分析就没有这种情况。这说明逃逸分析确实降低了堆内存的压力。

但是,逃逸分析只是栈上内存分配的前提,接下来还需要进行标量替换才能真正实现。

所谓标量,就是指JVM中无法再细分的数据,比如int、long、reference等。相对地,能够再细分的数据叫做聚合量。

仍然考虑上面的例子,MyObject就是一个聚合量,因为它由两个标量a、b组成。通过逃逸分析,JVM会发现myObject没有逃逸出allocate()方法的作用域,标量替换过程就会将myObject直接拆解成a和b,也就是变成了:

static void allocate() {

int a = 2019;

double b = 2019.0;

可见,对象的分配完全被消灭了,而int、double都是基本数据类型,直接在栈上分配就可以了。所以,在对象不逃逸出作用域并且能够分解为纯标量表示时,对象就可以在栈上分配。

JVM提供了参数-XX: EliminateAllocations来开启标量替换,默认仍然是开启的。显然,如果把它关掉的话,就相当于禁止了栈上内存分配,只有逃逸分析是无法发挥作用的。

在Debug版JVM中,还可以通过参数-XX: PrintEliminateAllocations来查看标量替换的具体情况。

除了标量替换之外,通过逃逸分析还能实现同步消除(synchronization elision),当然它与本文的主题无关了。

举个例子:

private void someMethod() {

Object lockObject = new Object();

synchronized (lockObject) {

System.out.println(lockObject.hashCode());

}

lockObject这个锁对象的生命期只在someMethod()方法中,并不存在多线程访问的问题,所以synchronized块并无意义,会被优化掉:

private void someMethod() {

Object lockObject = new Object();

System.out.println(lockObject.hashCode());

小编分类整理了许多java进阶学习材料和BAT面试题,需要资料的请私信小编, 就能领取2019年java进阶学习资料和BAT面试题以及《Effective Java》(第3版)电子版书籍。

    推荐阅读
  • 天津国字头大学最新消息(这所才是天津考生最爱的本地大学)

    我们以江西省的考生为例,来进行说明。上图为天津市高校在江西省的录取分排序。从数据来看,中国民航大学的录取分数非常低,远远低于天津工业大学、天津师范大学,连天津商业大学都不如。

  • 焖烧壶怎么选(如何选择焖烧壶)

    焖烧杯采用的304不绣钢与保温杯的材质差不多,只是焖烧杯的真空保温处理工艺会更强一些,保温时长更久。看品牌,焖烧杯近两年兴起,各大商家也追风推出这类产品,市面上的焖烧杯鱼龙混杂,价格也不一,很容易就买到假冒劣质品,不仅焖不出美味的食物,还有可能影响健康。看方便性,焖烧杯一般都是外出携带使用,所以携带方便的焖烧杯是首选。

  • 牛仔裤改造手工制作(废旧牛仔裤剪一剪)

    今天和小爱一起来赏析做出拼布后是什么效果,拿出来,剪剪裁裁拼一拼,就能变身拼布了。拼布靠垫步骤一:收集颜色深浅不同的牛仔裤两至三条,将牛仔裤的裤筒部位的面临剪成10cm*10cm的方块。好了,让这美丽的手工改造,去温暖温暖我们的生活。欢迎大家留言评论,文章素材图片整理互联网,分享大家学习使用。关注@我爱DIY,每天分享生活好玩资讯,创意,手工DIY教程。

  • 自己如何赞美自己(如何学会赞美自己)

    我们一起去了解并探讨一下这个问题吧!赞美的重要性就是让自己感觉原来自己不比别人差,别人能做的事,自己肯定也能做到,赞美,是一个人的动力,更是郁闷人的“良药”。没有十全十美,人都有优点和缺点,青少年要学会克服郁闷,从学会赞美自己开始。

  • 枫叶是什么颜色(枫叶颜色简述)

    下面更多详细答案一起来看看吧!枫叶是什么颜色枫叶在秋天来临之前基本上都是绿色的,但是到了秋天以后,它的颜色会从绿色变成黄色在逐渐变成红色,十分具有观赏价值。本身枫叶的形状就像是一个人的手掌,十分的小巧精致。火红的枫树叶,仿佛为秋日的凋零而带来了一丝生机。

  • 满江红豫剧伴奏(豫剧电音满江红配乐何以如此)

    春节档票房冠军、张艺谋执导电影《满江红》配乐登上微博热搜。给《满江红》配乐的著名歌手韩红是首次担任电影配乐作曲,她邀请了河南洛阳豫剧院首席板胡琴师李洪超和洛阳知名乐队斑马森林联袂制作。张晓英出生于梨园世家,擅长豫剧、曲剧、越调、小品等,从黑头成功转型为彩旦,现属于河南省曲剧团演员。热爱戏曲的网友统计了《满江红》电影配乐中出现的豫剧经典唱段的出处。

  • 滑翔机德雷克斯勒经典动作(摆脱地心引力--)

    NBA有很多供人围炉夜话的江湖传说譬如张大帅一生御女两万金枪不倒加内特曾摸得篮板上沿又比如德雷克斯勒曾在升高扣篮赛上以3.6米的高度力压乔丹……大多理性人在听闻此类以讹传讹的故事后都会一笑置之:“X,纯属扯淡”但也会随即萌生“不过这货说不定还真能做到……”

  • 衬衣领子发黄怎么洗小窍门(衬衣领子发黄怎么洗)

    衬衣领子发黄怎么洗小窍门衬领的第一次污垢可用挥发油擦拭;第二、三次也均可使用上法.但一旦超出此次数,就应将衬衫脱下洗净。夏季,衬衣领子、袖口特别容易脏。洗涤时先将衣服充分浸湿,在衣领、袖口处均匀涂上一层牙膏。待药液挥发后,再将衬衫投入洗衣机内按常规洗涤,即可获得满意的清洁效果。衬衣硬领一般是麻布和树脂布做成的。这徉,才能保持衣领挺括、美观,且不会出褶皱。

  • 海贼王鹰眼能打过新七武海吗(七武海排名曝光)

    海贼王作为如今最火的热血动漫,经过长达二十多年的连载,已经进入到和之国篇。在海贼王里,七武海虽然是海贼,但名义上是隶属于世界政府,至今为止,七武海总共有11位,除去后面加入的四位之外,第一任七武海的实力排名一直都是海米争论的焦点话题之一。至今为止,熊出手的次数不多,但每一次都体现了他强大的实力。