就算你躺在沙发上三天不起, 拉不开窗帘, 因为决定不了穿哪双袜子哭个没完, 我也不会进行爱你.
没有什么大不了的. 这个地球有 75 亿人, 就有 75 亿种失常.
前言
面试的时候 必问 JVM
, 淦! 咱们筹备好好温习吧, 加油! 奇怪的常识又减少了呐.
1. JVM 内存模型
依据 JVM 标准,JVM 内存共分为虚拟机栈,堆,办法区,程序计数器,本地办法栈五个局部
程序计数器 (线程公有):
是以后线程锁执行字节码的行号治期间,每条线程都有一个独立的程序计数器,这类内存也称为“线程公有”的内存。正在执行 java 办法的话,计数器记录的是虚拟机字节码指令的地址(以后指令的地址)。如果是 Natice 办法,则为空。
java 虚拟机栈
也是 线程公有的
。
每个办法在执行的时候也会创立一个栈帧,存储了 局部变量,操作数,动静链接,办法返回地址
。
每个办法从调用到执行结束,对应一个栈帧在虚拟机栈中的入栈和出栈。
通常所说的栈,个别是指在虚拟机栈中的局部变量局部。
局部变量所需内存在编译期间实现调配,
如果线程申请的栈深度大于虚拟机所容许的深度,则 StackOverflowError。
如果虚拟机栈能够动静扩大,扩大到无奈申请足够的内存,则 OutOfMemoryError。
本地办法栈
本地办法栈(Native Method Stacks)与虚拟机栈所施展的作用是十分类似的,其
区别不过是虚拟机栈为虚拟机执行 Java 办法(也就是字节码)服务,而本地办法栈则
是为 虚拟机应用到的 Native 办法服务
。虚拟机标准中对本地办法栈中的办法应用的语
言、应用形式与数据结构并没有强制规定,因而具体的虚拟机能够自在实现它。甚至
有的虚拟机(譬如 Sun HotSpot 虚拟机)间接就把本地办法栈和虚拟机栈合二为一。
与虚拟机栈一样,本地办法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError
异样
Java 堆 (线程共享)
被所有线程共享的一块内存区域,在虚拟机启动的时候创立,用于寄存对象实例。
对能够依照可扩大来实现(通过 -Xmx 和 -Xms 来管制)
当队中没有内存可调配给实例,也无奈再扩大时,则抛出 OutOfMemoryError 异样。
办法区 (线程共享)
被所有办法线程共享的一块内存区域。
用于存储曾经被虚拟机加载的类信息,常量,动态变量等。
这个区域的内存回收指标次要针对常量池的回收和堆类型的卸载。 JDK8 HotSpot JVM
将移除永恒区,应用本地内存来存储类元数据信息并称之为:元空间(Metaspace)
。这意味着不会再有 java.lang.OutOfMemoryError: PermGen 问题,也不再须要你进行调优及监控内存空间的应用。
二、空间调配担保(就像贷款一样)-XX:HandlerPromotionFailure
Minor GC 之前查看
老年代最大可用间断空间是否 > 新生代所有对象总空间。
大于 —-> Minor GC
不大于—> 老年代最大可用间断空间是否 > 历次降职到老年代对象的均匀大小
大于 —–> Minor GC
不大于 —–> Full GC
-XX:HandlerPromotionFailure
注:Minor GC 是针对新生代的垃圾回收,MajorGC 是老年代的,Full GC 是 Minor GC 加上 MajorGC
堆内存划分
新生代
所有 new 的对象都在堆外面。对象优先调配到 Eden 区,能够通过参数 -XX:SurviviorRatio= 8 来指定 Eden 区和 Survivior 的比例,这样划分的根据和益处是依据 gc 的回收算法来定的。做到了内存利用率高。
通过几次(能够通过参数设置)minor gc 还存活的对象进入 S0,再通过几次 minor gc 还存活进入 S1,达到 minor gc 的阈值(-XX:MaxTenuringThreshold=?)进入老年代。
还有就是动静对象年龄断定,雷同年龄所有对象的大小总和 > survivor 空间的一半,间接升级老年代,不须要进行年龄阈值的判断。
这个区域的现实是 98% 以上的对象可能被回收。
老年代
大对象(-XX:PretenureSizeThreadshold=1024)间接进入老年代,在新生代外面长期存活的对象进入老年代。老年代对应的是 major gc。
永恒代
蕴含元数据信息,如 class,method 的 detail 信息
在 Java1.8 时, 删除了办法区, 减少了元空间, 如下图:
永恒代与元空间
PermGen 空间情况
:这部分内存空间将全副移除。JVM 的参数:PermSize 和 MaxPermSize 会被疏忽并给出正告(如果在启用时设置了这两个参数)。
Metaspace 容量
: 默认状况下,类元数据只受可用的本地内存限度
(容量取决于是 32 位或是 64 位操作系统的可用虚拟内存大小)。 新参数(MaxMetaspaceSize)用于限度本地内存调配给类元数据的大小
。如果没有指定这个参数,元空间会在运行时依据须要动静调整。
另外,对于僵死的类及类加载器的垃圾回收将在元数据应用达到“MaxMetaspaceSize”参数的设定值时进行。适时地 监控和调整元空间
对于减小垃圾回收频率和缩小延时是很有必要的。继续的元空间垃圾回收阐明,可能存在类、类加载器导致的内存透露或是大小设置不适合。
永恒代
在 JDK8 之前的 HotSpot JVM,寄存这些”永恒的”的区域叫做“永恒代 (permanent generation)”。永恒代是一片间断的堆空间,在 JVM 启动之前通过在命令行设置参数-XX:MaxPermSize
来设定永恒代最大可调配的内存空间,默认大小是 64M(64 位 JVM 因为指针收缩,默认是 85M)
。永恒代的垃圾收集是和老年代(old generation) 捆绑在一起
的,因而无论谁满了,都会触发永恒代和老年代的垃圾收集。不过,一个显著的问题是,当 JVM 加载的类信息容量超过了参数 -XX:MaxPermSize 设定的值时,利用将 会报 OOM 的谬误
(对于这句话,译者的了解是:32 位的 JVM 默认 MaxPermSize 是 64M,而 JDK8 里的 Metaspace,也能够通过参数 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 设定大小,但如果不指定 MaxMetaspaceSize 的话,Metaspace 的大小仅 受限于 native memory 的残余大小
。也就是说永恒代的最大空间肯定得有个指定值,而如果 MaxPermSize 指定不当,就会 OOM)。
从 JDK7 开始
永恒代的移除工作,储存在永恒代的一部分数据曾经转移到了 Java Heap 或者是 Native Heap。但永恒代依然存在于 JDK7,并没有齐全的移除:符号援用 (Symbols) 转移到了 native heap; 字面量 (interned strings) 转移到了 java heap; 类的动态变量 (class statics) 转移到了 java heap。
在 JDK7 update 4 即随后的版本中,提供了残缺的反对对于 Garbage-First(G1)
垃圾收集器,以取代在 JDK5 中公布的 CMS 收集器
。应用 G1,PermGen 仅仅在FullGC(stop-the-word,STW)
时才会被收集。G1 仅仅在 PermGen 满了或者利用分配内存的速度比 G1 并发垃圾收集速度快的时候才触发 FullGC。
而对于 CMS 收集器,通过开启布尔参数 -XX:+CMSClassUnloadingEnabled 来并发对 PermGen 进行收集。对于 G1 没有相似的选项,G1 只能通过 FullGC,stop the world, 来对 PermGen 进行收集。
永恒代在 JDK8 中被齐全的移除了。所以永恒代的参数 -XX:PermSize 和 -XX:MaxPermSize 也被移除了。
元空间
在 JDK8 中,classe metadata(the virtual machines internal presentation of Java class), 被存储在叫做 Metaspace 的 native memory。一些新的 flags 被退出:
1、-XX:MetaspaceSize
,class metadata 的初始空间配额,以 bytes 为单位,达到该值就会触发垃圾收集进行类型卸载,同时 GC 会对该值进行调整:如果开释了大量的空间,就适当的升高该值;如果开释了很少的空间,那么在不超过 MaxMetaspaceSize(如果设置了的话),适当的进步该值。
2、-XX:MaxMetaspaceSize
,能够为 class metadata 调配的最大空间。默认是没有限度的。
3、-XX:MinMetaspaceFreeRatio
, 在 GC 之后,最小的 Metaspace 残余空间容量的百分比,缩小为 class metadata 调配空间导致的垃圾收集
4、-XX:MaxMetaspaceFreeRatio
, 在 GC 之后,最大的 Metaspace 残余空间容量的百分比,缩小为 class metadata 开释空间导致的垃圾收集
5、默认状况下,class metadata
的调配仅受限于可用的 native memory 总量。能够应用 MaxMetaspaceSize 来限度可为 class metadata 调配的最大内存。当 class metadata 的应用的内存达到 MetaspaceSize(32 位 clientVM 默认 12Mbytes,32 位 ServerVM 默认是 16Mbytes)时就会对死亡的类加载器和类进行垃圾收集。设置 MetaspaceSize 为一个较高的值能够推延垃圾收集的产生。
这里科普下,在 Windows 下称为虚拟内存(virtual memory), 在 Linux 下称为替换空间(swap space), 用于当零碎须要更多的内存资源而物理内存曾经满了的状况下,将物理内存中不沉闷的页转移到磁盘上的替换空间中。
在 JDK8,Native Memory,包含 Metaspace 和 C -Heap。
永恒代的移除对最终用户意味着什么?
因为类的元数据能够在本地内存 (native memory) 之外调配, 所以其最大可利用空间是整个零碎内存的可用空间。这样,你将不再会遇到 OOM 谬误,溢出的内存会涌入到替换空间。最终用户能够为类元数据指定最大可利用的本地内存空间,JVM 也能够减少本地内存空间来满足类元数据信息的存储。
注:永恒代的移除并不象征者类加载器泄露的问题就没有了。因而,你依然须要监控你的生产和打算,因为内存泄露会耗尽整个本地内存,导致内存替换(swapping),这样只会变得更糟。
创作不易, 如果本篇文章能帮忙到你, 请给予反对, 赠人玫瑰, 手有余香,虫虫蟹蟹观众姥爷了