天道不一定酬所有勤
但是,天道只酬勤

求你了,别再说Java对象都是在堆内存上分配空间的了!

可能是2020年最全的超硬核Java 面试 “备战” 资料!(超过500页)

Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解。可以说,关于JVM的相关知识,基本是每个Java开发者必学的知识点,也是面试的时候必考的知识点。

在JVM的内存结构中,比较常见的两个区域就是堆内存和栈内存(如无特指,本文提到的栈均指的是虚拟机栈),关于堆和栈的区别,很多开发者也是如数家珍,有很多书籍,或者网上的文章大概都是这样介绍的:

1、堆是线程共享的内存区域,栈是线程独享的内存区域。

2、堆中主要存放对象实例,栈中主要存放各种基本数据类型、对象的引用。

但是,作者可以很负责任的告诉大家,以上两个结论均不是完全正确的。

在我之前的文章《Java堆内存是线程共享的!面试官:你确定吗?》中,介绍过了关于堆内存并不是完完全全的线程共享有关的知识点,本文就第二个话题来探讨一下。

对象内存分配

在《Java虚拟机规范》中,关于堆有这样的描述:

在Java虚拟机中,堆是可供各个线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。

在《Java堆内存是线程共享的!面试官:你确定吗?》文章中,我们也介绍过,一个Java对象在堆上分配的时候,主要是在Eden区上,如果启动了TLAB的话会优先在TLAB上分配,少数情况下也可能会直接分配在老年代中,分配规则并不是百分之百固定的,这取决于当前使用的是哪一种垃圾收集器,还有虚拟机中与内存有关的参数的设置。

但是一般情况下是遵循以下原则的:

  • 对象优先在Eden区分配
    • 优先在Eden分配,如果Eden没有足够空间,会触发一次Monitor GC
  • 大对象直接进入老年代
    • 需要大量连续内存空间的Java对象,当对象需要的内存大于-XX:PretenureSizeThreshold参数的值时,对象会直接在老年代分配内存。

但是,虽然虚拟机规范中是有着这样的要求,但是各个虚拟机厂商在实现虚拟机的时候,可能会针对对象的内存分配做一些优化。这其中最典型的就是HotSpot虚拟机中的JIT技术的成熟,使得对象在堆上分配内存并不是一定的。

其实在《深入理解Java虚拟机》中,作者也提出过类似的观点,因为JIT技术的成熟使得”对象在堆上分配内存”就不是那么绝对的了。但是书中并没有展开介绍到底什么是JIT,也没有介绍JIT优化到底做了什么。那么接下来我们就来深入了解一下:

JIT 技术

我们大家都知道,通过 javac 将可以将Java程序源代码编译,转换成 java 字节码,JVM 通过解释字节码将其翻译成对应的机器指令,逐条读入,逐条解释翻译。这就是传统的JVM的解释器(Interpreter)的功能。很显然,Java编译器经过解释执行,其执行速度必然会比直接执行可执行的二进制字节码慢很多。为了解决这种效率问题,引入了 JIT(Just In Time ,即时编译) 技术。

Hollis为了防爬虫以及未经授权的恶意转载,此处内容已被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“验证码”,获取验证码。在微信里搜索“Hollis”或者“hollischuang”或者微信扫描右侧二维码都可以关注本站微信公众号。

(全文完) 欢迎关注『Java之道』微信公众号
赞(5)
如未加特殊说明,此网站文章均为原创,转载必须注明出处。HollisChuang's Blog » 求你了,别再说Java对象都是在堆内存上分配空间的了!
分享到: 更多 (0)

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    前面内容说明了JIT使用到了“计数器”,所以刚开始运行时肯定会创建一定数量(8万多)的对象,然后才触发了JIT进而优化执行后面的91.N万次执行过程了,所以基于计数器的JIT永远不可能优化为0次就是必然的了。

    zigzagroad6个月前 (03-18)回复

HollisChuang's Blog

联系我关于我