一、Java虚拟机逻辑回收机制
1、Java垃圾回收器
Java垃圾回收器是Java虚拟机(JVM)的三个重要模块(另外两个是解释器和多线程机制)之一,为应用程序提供内存的自动分配(Memory Allocation)、自动回收(Garbage Collect)功能,这两个操作都发生在Java堆上(一段内存快)。
某一个时点,一个对象如果有一个以上的引用(Rreference)指向它,那么该对象就为活着的(Live),否则死亡(Dead),视为垃圾,可被垃圾回收器回收再利用。
回收操作需要消耗CPU、线程、时间等资源,所以容易理解的是垃圾回收操作不是实时的发生(对象死亡马上释放),当内存消耗完或者是达到某一个指标(Threshold,使用内存占总内存的比列,比如0.75)时,触发垃圾回收操作。有一个对象死亡的例外,java.lang.Thread类型的对象即使没有引用,只要线程还在运行,就不会被回收。
依据统计分析可知,Java(包括一些其它高级语言)里面大多数对象生命周期都是短暂的,所以把Java内存分代管理。分代的目的无非就是为不同代的内存块运用不同的管理策略(算法),从而最大化性能。相对于年老代,通常年轻代要小很多,回收的频率高,速度快。年老代则回收频率低,耗时长。内存在年轻代里面分配,年轻代里面的对象经过多个回收周期依然存活的会自动晋升到年老代。
2、垃圾回收类型
所有的回收器类型都是基于分代技术。Java HotSpot虚拟机包含三代,年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation)。
(1)永久代
存储类、方法以及它们的描述信息。可以通过-XX:PermSize=64m和-XX:MaxPermSize=128m两个可选项指定初始大小和最大值。通常 我们不需要调节该参数,默认的永久代大小足够了,不过如果加载的类非常多,不够用了,调节最大值即可。
(2)年老代
主要存储年轻代中经过多个回收周期仍然存活从而升级的对象,当然对于一些大的内存分配,可能也直接分配到永久代(一个极端的例子是年轻代根本就存不下)。
(3)年轻代
绝大多数的内存分配回收动作都发生在年轻代。如下图所示, 年轻代被划分为三个区域,原始区(Eden)和两个小的存活区(Survivor),两个存活区按功能分为From和To。绝大多数的对象都在原始区分配,超过一个垃圾回收操作仍然存活的对象放到存活区。
说明:年轻代里面的对象经过多个回收周期依然存活的会自动晋升到年老代。年老代多个回收周期依然存活的会成为永久代。
3、JAVA虚拟机垃圾回收机制
(1)Minor GC
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。这一定义既清晰又易于理解。但是,当发生Minor GC事件的时候,当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。
所有的 Minor GC 都会触发“全世界的暂停(stop-the-world)”即停止应用程序的线程。
(2)Major GC vs Full GC
指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC 。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。
当发生Full GC时,也会触发“全世界的暂停(stop-the-world)”即停止应用程序的线程。并且持续时间长。
二、Java排错三剑客
1、jstack 查看指定的java进程的线程栈的相关信息
jstack [-l] <pid>
jstack -F [-m] [-l] <pid>
-l:long listings,会显示额外的锁信息,因此,发生死锁时常用此选项
-m:混合模式,既输出java堆栈信息,也输出C/C++堆栈信息
-F:当使用“jstack -l PID"无响应,可以使用-F强制输出信息
例子:
(1)查看pid
ps aux|grep tomcat
root 2745 0.6 19.0 2333928 190144 pts/0 Sl 08:58 3:57 /usr/java/jdk1.8.0_144/jre/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.endorsed.dirs=/usr/local/tomcat/endorsed -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
root 9424 0.0 0.0 112648 960 pts/0 S+ 19:47 0:00 grep --color=auto tomcat
可以看到pid为 2745
(2)jstack -l 2745
2、jstat 输出指定的java进程的统计信息
jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
# jstat -options
-class:class loader 类加载统计
-compiler:JIT 编译统计
-gc:gc
-gcnew:新生代
-gcold:老年代
-printcompilation JVM编译方法统计
[<interval> [<count>]]
interval:时间间隔,单位是毫秒;
count:显示的次数;
例子:
(1)jstat -class 2745 1000 1000 以秒为间隔显示1000次,类加载统计
(2)jstat -gc 2745 1000 1000 可以看大GC和小GC的发生频率。
S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)
EC、EU:Eden区容量和使用量
OC、OU:年老代容量和使用量
PC、PU:永久代容量和使用量
YGC、YGT:年轻代GC次数和GC耗时
FGC、FGCT:Full GC次数和Full GC耗时
GCT:GC总耗时
3、jmap Memory Map, 用于查看堆内存的使用状态
非常消耗资源,一般先把机器摘下,再执行命令
jmap [option] <pid> 查看堆空间的详细信息:
jmap -heap <pid>查看堆内存中的对象的数目:
jmap -histo <pid>
live:只统计活动对象;
保存堆内存数据至文件中,而后使用jvisualvm或jhat进行查看:
jmap -dump:<dump-options> <pid>
dump -options:
live dump only live objects; if not specified, all objects in the heap are dumped.
format=b binary format
file=<file> dump heap to <file>
例子:jmap -dump:format=b,file=/app/dump 2745
cd /app
因为是二进制文件不能直接看,需要转换
jhat dump
ss -ntl 会监听7000端口,可以在浏览器查看