方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,描述为堆的一个逻辑部分,称为非堆(HotSpot中也称为永久代)。该区域包含运行时常量池。Java8之后改为元空间(MetaSpace),直接分配物理内存。对这块内存的GC条件很苛刻,基本认为不会进行。
堆是被所有线程共享的一块内存区域,是JVM管理的内存中最大的一块,在虚拟机启动时创建。几乎所有的对象实例以及数组都要在堆分配内存。垃圾收集器管理的主要区域。
也称为线程本地栈,每个线程拥有自己独立的一份,每调用一个方法都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈和出栈的过程。
为虚拟机使用到的Native方法服务。与虚拟机栈发挥的作用相似。有的虚拟机(如HotSpot)把本地方法栈和虚拟机栈合二为一了。
是当前线程所执行的字节码的行号指示器。
一般是指堆内存(方法区很少)中不会再使用到的对象,通俗讲就是没有引用指向它的对象。
引用的概念分类
算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器值为0的对象就是不可能再被使用的。
优点:实现简单,效率也高。
缺点:很难解决对象之间相互循环引用的问题。
思路:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GCRoots到这个对象不可达)时,则证明此对象是不可用的。
如图,对象object4、object5、object6虽然互相有关联,但是它们到GCRoots是不可达的,所以它们将会被判定为可回收的对象。
在Java语言中可作为GC Roots的对象包括以下几种:
如果所示:首先标记出所有需要回收的对象,在标记完成后统一回收被标记的对象。
缺点:
如图所示:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存使用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂问题,只要移动堆顶指针按顺序分配内存即可。
优点:
缺点:
如图所示: 让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
1、CMS(Concurrent Mark Sweep)收集器
以获取最短回收停顿时间为目标。
优点:并发收集、低停顿
缺点:对CPU资源敏感、无法处理浮动垃圾、产生大量空间碎片
2、G1(Garbag-First)收集器
优点:
更多请查阅https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/
3、Java平台,标准版HotSpot虚拟机垃圾收集调优指南
<<<<<<<<<<摘自Oracle官网>>>>>>>>>>
Java HotSpot VM包括三种不同类型的收集器,每种收集器具有不同的性能特征。
并行压缩是一种使并行收集器能够并行执行主要集合的功能。如果没有并行压缩,主要集合将使用单个线程执行,这可能会显着限制可伸缩性。如果-XX:+UseParallelGC已指定选项,则默认启用并行压缩。关闭它的选项是-XX:-UseParallelOldGC。
选择收集器
除非您的应用程序具有相当严格的暂停时间要求,否则首先运行您的应用程序并允许VM选择收集器。如有必要,请调整堆大小以提高性能。如果性能仍不符合您的目标,请使用以下指南作为选择收集器的起点。
这些指南仅提供了选择收集器的起点,因为性能取决于堆的大小,应用程序维护的实时数据量以及可用处理器的数量和速度。暂停时间对这些因素特别敏感,因此前面提到的1秒阈值只是近似值:并行收集器在许多数据大小和硬件组合上将经历超过1秒的暂停时间; 相反,并发收集器可能无法在某些组合上保持短于1秒的暂停。
如果推荐的收集器未达到所需性能,请首先尝试调整堆和生成大小以满足所需目标。如果性能仍然不足,那么尝试使用不同的收集器:使用并发收集器来减少暂停时间,并使用并行收集器来提高多处理器硬件的总吞吐量。
4、内存分配与回收策略
当前虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法:根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。
对象优先在Eden分配
大对象直接进入老年代(-XX:PretenureSizeThreshold参数)
长期存活的对象将进入老年代(-XX:MaxTenuringThreshold参数)
动态对象年龄判定
空间分配担保
5、Java对象的分配
栈上分配
线程本地分配:TLAB(Thread Local Allocation Buffer):-XX:+UseTLAB
老年代
Eden
-:标准参数,所有JVM都应该支持
-X:非标,每个JVM实现都不同
-XX:不稳定参数,下一个版本可能会取消
图片来源于网络常用设置套路
1、-Xms和-Xmx设置相近或相等:避免Java堆内存自动扩容时带来的性能消耗 。
2、-Xss:设置小,执行的线程可以多一些。设置大,方法的调用可以深一些。
1、Java对象的分配
当关闭逃逸分析,关闭标量替换,关闭线程本地程序时
改下JVM参数
2、用Runtime类“大致”计算内存情况
3、内存溢出分析
可以看到,byte[]占了大部分内存。
4、线程栈大小测试
1、JConsole:Java监视与管理控制台
2、VisualVM:多合一故障处理工具
无监控,不调优!!!