JAVA虚拟机垃圾回收机制和JAVA排错三剑客

一、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

JAVA虚拟机垃圾回收机制和JAVA排错三剑客

2、jstat  输出指定的java进程的统计信息   

jstat -help|-options

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

    # jstat -options

            -classclass loader  类加载统计

            -compilerJIT 编译统计

            -gcgc

            -gcnew新生代

            -gcold老年代

            -printcompilation JVM编译方法统计

        [<interval> [<count>]]

        interval时间间隔,单位是毫秒;

                count显示的次数;

     例子:

    (1)jstat -class 2745 1000 1000    以秒为间隔显示1000次,类加载统计

            JAVA虚拟机垃圾回收机制和JAVA排错三剑客

        (2)jstat -gc 2745 1000 1000  可以看大GC和小GC的发生频率。

              JAVA虚拟机垃圾回收机制和JAVA排错三剑客

    S0CS1CS0US1USurvivor 0/1区容量(Capacity)和使用量(Used)

    ECEUEden区容量和使用量

    OCOU年老代容量和使用量

    PCPU永久代容量和使用量

    YGCYGT年轻代GC次数和GC耗时

    FGCFGCTFull GC次数和Full GC耗时

    GCTGC总耗时

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

            JAVA虚拟机垃圾回收机制和JAVA排错三剑客

            cd /app

            因为是二进制文件不能直接看,需要转换

            jhat  dump   

            ss -ntl  会监听7000端口,可以在浏览器查看

            JAVA虚拟机垃圾回收机制和JAVA排错三剑客



本文转自 hawapple 51CTO博客,原文链接:http://blog.51cto.com/guanm/2044858

上一篇:前端小锦囊(一) 如果后端给你返回了一个字符串类型的表达式该如何获取到结果?eval() 函数详解


下一篇:两种方式创建你自己的 Docker 基本映像