调优命令有哪些?
Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo
- jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
- jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
- jmap,JVM Memory Map命令用于生成heap dump文件
- jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
- jstack,用于生成java虚拟机当前时刻的线程快照。
- jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数
JVM的调优,主要针对启动命令,设置堆大小、垃圾收集器
·-Xmx 设置应用程序(不是JVM)能够使用的最大内存数(相当于-XX:MaxHeapSize)
默认值为你当前机器最大内存的1/4
·-Xms 用来设置程序初始化的时候内存堆的大小(相当于-XX:MaxNewSize)
默认值为你当前机器最大内存的1/64
Oracle 建议设置最小堆大小(-Xms)等于最大堆大小(-Xmx)以最小化垃圾收集。
·-XX:NewSize:新生代大小 设置-XX:NewSize为堆大小的四分之一。设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
·-XX:NewRatio 新生代和老生代占比
·-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比
·-XX:+UseG1GC: 使用G1收集器
在生产环境中,将最小堆大小和最大堆大小设置为相同的值,以防止浪费用于不断增长和收缩堆的 VM 资源。
https://docs.oracle.com/middleware/11119/wls/PERFM/jvm_tuning.htm#i1146060
https://docs.oracle.com/middleware/11119/wls/PERFM/jvm_tuning.htm#i1146849
JVM内存分布
元空间:
本质和永久代类型,不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。(提高Full GC的性能)
存放永久代、类加载信息、静态变量、常量等数据。
虚拟机栈:
每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。
本地方法栈:
本地方法栈执行native方法。
程序计数器:
记录线程的执行位置,线程切换时需要换到原来的位置。
堆内存(GC堆):
所有的对象和数组都在堆上进行分配。
老年代:2/3的堆空间
新生代:1/3的堆空间
eden区:8/10的新生代空间
survivor 0:1/10的新生代空间
survivor 1:1/10的新生代空间
Java 1.7把字符串常量池从永久代中剥离出来,存放在堆空间中。
直接内存:
直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中农定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
垃圾回收GC
新生代
是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。新生代又分为Eden区、SurvivorFrom、ServivorTo三个区。
Young区触发MinoGC(复制->清空>互换)
使用Parallel scavenge垃圾收集器(Java1.8默认的新生代垃圾收集器),使用的复制算法,多线程执行,高效。
- Eden、SurvivorFrom存活的对象复制到SurvivorTo,年龄+1,SurvivorTo空间不足会放进老年代
- 清空Eden、SurvivorFrom
- SurvivorTo和SurvivorFrom不停互换,下次清除Eden和SurvivorTo
老年代
存放生命周期长的内存对象。
Old区触发MajorGC(标记整理算法,如果使用标记清除算法,内存是不连续的,产生内存碎片)
使用Parallel Old 收集器(Java1.8默认的新生代垃圾收集器),多线程标记整理算法
- 标记出存活对象
- 将存活对象移向内存的一端
- 清除边界外的对象
永久代
存放Class和Meta(元数据)的信息,GC不会在主程序运行期堆永久区域进行清理。
CMS(Concurrent Mark Sweep)
一种以获取最短回收停顿时间为目标的收集器,基于标记-清除算法的,CMS只会删除无用对象,不会对内存做压缩,会造成内存碎片。仅作用于老年代收集。在1.9后将其废除
G1
是目前垃圾收集器理论发展的最前沿成果,jdk 9 默认使用的就是G1,兼顾吞吐量和停顿时间的 GC 实现,相比与 CMS 收集器, G1 收集器两个最突出的改进是:
- 基于标记-整理算法,不产生内存碎片。
- 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。
什么是GC,和fullGC?
MinorGC负责young区,新生代区不足时触发
MajorGC负责old区,老年代区不足时触发
fullGC:收集young gen、old gen、perm gen,
触发机制
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
2. 当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载。
3. 调用System.gc时,系统建议执行Full GC,但是不必然执行
引用计数算法
堆中每个对象都有一个引用计数。
当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1,当对象引用失效则减1),计数器为0时可以被GC回收;
缺点:存在两个对象互相引用,但已经没有实例指向它们,无法被清除。
可达性算法(GC Roots Tracing)
从GC Roots作为起点开始搜索,那么整个连通图中的对象便都是活对象,对于GC Roots无法到达的对象变成了垃圾回收的对象,随时可被GC回收。
逃逸分析
逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术。这是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。
从jdk 1.7开始已经默认开始逃逸分析,如需关闭,需要指定-XX:-DoEscapeAnalysis
所以,如果以后再有人问你:是不是所有的对象和数组都会在堆内存分配空间?
那么你可以告诉他:不一定,随着JIT编译器的发展,在编译期间,如果JIT经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。但是这也并不是绝对的。就像我们前面看到的一样,在开启逃逸分析之后,也并不是所有User对象都没有在堆上分配。
总结:判断如果没有引用的对象,不在堆中生成,避免内存浪费,JIT的逃逸分析带来的好处。
逃逸分析并不成熟
关于逃逸分析的论文在1999年就已经发表了,但直到JDK 1.6才有实现,而且这项技术到如今也并不是十分成熟的。
其根本原因就是无法保证逃逸分析的性能消耗一定能高于他的消耗。虽然经过逃逸分析可以做标量替换、栈上分配、和锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的,这其实也是一个相对耗时的过程。
一个极端的例子,就是经过逃逸分析之后,发现没有一个对象是不逃逸的。那这个逃逸分析的过程就白白浪费掉了。
虽然这项技术并不十分成熟,但是他也是即时编译器优化技术中一个十分重要的手段。