【JVM从小白学成大佬】2.Java虚拟机运行时数据区 | 猿人谷
Opened this issue · 7 comments
https://yuanrengu.com/2020/d6aff7a6.html
这里我们先说句题外话,相信大家在面试中经常被问到介绍Java内存模型,我在面试别人时也会经常问这个问题。但是,往往都会令我比较尴尬,我还话音未落,面试者就会“背诵”一段(Java虚拟机是由堆、方法区、虚拟机栈,吧啦吧啦。。。),估计心里还一脸自豪的想幸好哥提前在网上搜过,早有准备。每每这个时候,我都不忍心打断,因为“背诵”的真的太顺畅了! 这也怪不得面试者,首先Java虚拟机方面的知识,对中高级程
有没有老铁被面试官挖坑问“堆是线程共享的内存区域吗?”
Java堆内存中按照分代年龄来划分,分为Young区和Old区,对象分配首先会到Young区。之所以会这么划分是因为如果整个堆只有一个区的话,那么垃圾回收的时候每次都需要把堆内所有对象都扫描一遍,浪费性能。而其实大部分Java对象的生命周期都是很短的,一旦一个对象回收很多次都回收不掉,可以认为下一次垃圾回收的时候可能也回收不掉,所以Young区和Old区的垃圾回收可以分开进行,只有当Young区在进行垃圾回收之后还是没有腾出空间,那么再去触发Old区的垃圾回收。
首先还是在Eden区分配空间,Eden区满了之后触发GC,GC之后把幸存对象 复制到S0区(S1区是空的),然后继续在Eden区分配对象,再次触发GC之后如果发现S0区放不下了(产生空间碎片,实际还有空间),那么就把S0区对象复制到S1区,并把幸存对象也复制到S1区,这时候S0区是空的了,并依次反复操作,假如说S0区或者S1区空间对象复制移动了之后还是放不下,那就说明这时候是真的满了,那就去老年区借点空间过来(这就是担保机制,老年代需要提供这种空间分配担保),假如说老年区空间也不够了,那就会触发Full GC,如果还是不够,那就会抛出OutOfMemeoyError异常了。
首先还是在Eden区分配空间,Eden区满了之后触发GC,GC之后把幸存对象 复制到S0区(S1区是空的),然后继续在Eden区分配对象,再次触发GC之后如果发现S0区放不下了(产生空间碎片,实际还有空间),那么就把S0区对象复制到S1区,并把幸存对象也复制到S1区,这时候S0区是空的了,并依次反复操作,假如说S0区或者S1区空间对象复制移动了之后还是放不下,那就说明这时候是真的满了,那就去老年区借点空间过来(这就是担保机制,老年代需要提供这种空间分配担保),假如说老年区空间也不够了,那就会触发Full GC,如果还是不够,那就会抛出OutOfMemeoyError异常了。
为了确保S0和S1两个区域之间每次复制都能顺利进行,S0和S1两个区的大小必须要保持一致,而且同一时间有一个区域一定是空的。虽然说这种做法是会导致了一小部分空间的浪费,但是综合其他性能的提升来说,是值得的。
内存泄漏与内存溢出的关系:内存泄漏很容易导致内存溢出,但内存溢出不一定是内存泄漏导致的。
- 内存溢出(OOM):可用内存不足。
- 内存泄露:指本来无用的对象却继续占用内存,没有在恰当的时机释放占用的内存。
如果存在严重的内存泄露问题,随着时间的推移,则必然会引起内存溢出。
内存泄露一般是资源管理问题和程序bug,内存溢出则是内存空间不足和内存泄露的最终结果。
- 在 Java6 版本中,永久代在非堆内存区;到了 Java7 版本,永久代的静态变量和运行时常量池被合并到了堆中;而到了 Java8,永久代被元空间取代了。
- 在 HotSpot 虚拟机、Java7 版本中已经将永久代的静态变量和运行时常量池转移到了
堆
中,其余部分则存储在 JVM 的非堆内存
中,而 Java8 版本已经将方法区中实现的永久代去掉了,并用元空间(class metadata)代替了之前的永久代
,并且元空间的存储位置是本地内存。之前永久代的类的元数据存储在了元空间,永久代的静态变量(class static variables)以及运行时常量池(runtime constant pool)则跟 Java7 一样,转移到了堆中。
如果有一个类中定义了 String a="b"和 String c = new String(“b”),请问这两个对象会分别创建在 JVM 内存模型中的哪块区域呢?
- String a="b"可能创建
一个对象或者不创建对象
,如果"b"这个字符串在常量池里不存在会在常量池创建一个String对象"b",如果已经存在则a直接reference to这个常量池里的对象; - String c= new String("b")
至少创建一个对象,也可能两个
,因为用到new关键字,会在堆内在创建一个的String对象,它的值是"b"。同时,如果"b"这个字符串在常量池里不存在,会在常量池创建这个一个String对象"b"。