JVM运行时数据区
堆、方法区、虚拟机栈、本地方法栈、程序计数器
线程共享数据区:
堆:几乎所有对象实例都要在堆上分配,可以通过-Xmx -Xms来控制;
方法区:存放静态变量、常量(在运行时常量池中存放)、类信息、JIT编译后的代码,(在JDK的HotSpot虚拟机中,可以认为方法区就是永久代,但是在其他类型的虚拟机中,没有永久代的概念)可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值。
线程独有数据区:
虚拟机栈:存储当前线程运行方法所需要的数据、指令、返回地址,其中一个方法对应一个或多个栈帧(如果这个方法内部调用了别的方法的话就会有多个),栈帧中存储的有局部变量表、操作数栈、动态链接、出口;
本地方法栈:用于支持native方法的执行,存储了每个native方法调用的状态;
程序计数器:存储当前线程所执行的字节码的行号,几乎不占什么内存。
JVM内存模型(JMM)
堆(新生代(Eden区+FromSurvivor+ToSurvivor)、老年代)+非堆(方法区or永久代)
新生代Eden区和S1、S2内存大小比默认是8:1:1,
新生代老年代内存大小比默认是1:2(因为要存大对象,所以内存设置比较大)
这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。对不同的年代区,可以使用不同的算法进行垃圾回收处理。
垃圾回收,有两种算法:
- 引用计数法:其主要思想就是维护一个counter,当counter为0的时候认为对象没有被引用,可以被回收。不足之处就是无法收集循环引用的对象。
- 可达性分析法:从gc root根据引用关系来遍历整个堆并作标记,称之为mark,之后回收掉未被mark的对象,好处是解决了循环依赖这种『孤岛效应』。gc root可以是方法区中的常量引用的对象或者静态变量引用的对象,也可以是虚拟机栈中本地变量表中引用的对象,也可以是本地方法栈中的JNI(java native interface)引用的对象。
工作中经常打交道的,也是GC收集垃圾的主要区域。
JVM最而发生在这三个地方的垃圾回收
新生代呢,一般用复制算法。
老年代呢,一般是大对象,大多都不会随便死掉,也不怎么需要清理,所以就用标记整理法。
GC回收器粗略分为2种,一种是在新生代活动的,一种是在老年代活动的。
新生代中,有哪几种收集器?
serial(串行)、ParNew(并行)、Parallel Scavenge(并行),一般都使用parallel GC。
老年代呢?有哪几种收集器?
Serial Old(串行,与Parallel Scavenger搭配使用)
CMS(目标:获取最短回收停顿时间,标记-清除)
Parallel Old(并行)
G1收集器:并行、并发、分代;初始标记、并发标记、最终标记、筛选回收。
CMS和G1的区别是:G1不区分新生代和老年代,把堆空间分为大小想等的区域。
- Serial GC(-XX:+UseSerialGC):Serial GC使用简单的标记、清除、压缩方法对年轻代和年老代进行垃圾回收,即Minor GC和Major GC。Serial GC在client模式(客户端模式)很有用,比如在简单的独立应用和CPU配置较低的机器。这个模式对占有内存较少的应用很管用。
- Parallel GC(-XX:+UseParallelGC):除了会产生N个线程来进行年轻代的垃圾收集外,Parallel GC和Serial GC几乎一样。这里的N是系统CPU的核数。我们可以使用 -XX:ParallelGCThreads=n 这个JVM选项来控制线程数量。并行垃圾收集器也叫throughput收集器。因为它使用了多CPU加快垃圾回收性能。Parallel GC在进行年老代垃圾收集时使用单线程。
- Parallel Old GC(-XX:+UseParallelOldGC):和Parallel GC一样。不同之处,Parallel Old GC在年轻代垃圾收集和年老代垃圾回收时都使用多线程收集。
- 并发标记清除(CMS)收集器(-XX:+UseConcMarkSweepGC):CMS收集器也被称为短暂停顿并发收集器。它是对年老代进行垃圾收集的。CMS收集器通过多线程并发进行垃圾回收,尽量减少垃圾收集造成的停顿。CMS收集器对年轻代进行垃圾回收使用的算法和Parallel收集器一样。这个垃圾收集器适用于不能忍受长时间停顿要求快速响应的应用。可使用 -XX:ParallelCMSThreads=n JVM选项来限制CMS收集器的线程数量。
- G1垃圾收集器(-XX:+UseG1GC) G1(Garbage First):垃圾收集器是在Java 7后才可以使用的特性,它的长远目标时代替CMS收集器。G1收集器是一个并行的、并发的和增量式压缩短暂停顿的垃圾收集器。G1收集器和其他的收集器运行方式不一样,不区分年轻代和年老代空间。它把堆空间划分为多个大小相等的区域。当进行垃圾收集时,它会优先收集存活对象较少的区域,因此叫“Garbage First”。你可以在Oracle Garbage-FIrst收集器文档找到更多详细信息。
线上调优:
jdk提供的vm故障处理工具都比较实用,常用的jps,jstat,jmap,jstack以及可视化工具jconsole/visualvm,当然根据个人实际实用情况
jps – jps是用来查看JVM里面所有进程的具体状态, 包括进程ID,进程启动的路径等等
jstack -- 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。目前只有在Solaris和Linux的JDK版本里面才有。
jconsole – jconsole是基于Java Management Extensions (JMX)的实时图形化监测工具,这个工具利用了内建到JVM里面的JMX指令来提供实时的性能和资源的监控,包括了Java程序的内存使用,Heap size, 线程的状态,类的分配状态和空间使用等等。
jinfo – jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息,目前只有在Solaris和Linux的JDK版本里面才有。
jmap – jmap 可以从core文件或进程中获得内存的具体匹配情况,包括Heap size, Perm size等等,目前只有在Solaris和Linux的JDK版本里面才有。
jdb – jdb 用来对core文件和正在运行的Java进程进行实时地调试,里面包含了丰富的命令帮助您进行调试,它的功能和Sun studio里面所带的dbx非常相似,但 jdb是专门用来针对Java应用程序的。
jstat – jstat利用了JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控等等。
1、jps用来查看基于HotSpot的JVM里面中,所有具有访问权限的Java进程的具体状态, 包括进程ID,进程启动的路径及启动参数等等,与unix上的ps类似,只不过jps是用来显示java进程,可以把jps理解为ps的一个子集。 使用jps时,如果没有指定hostid,它只会显示本地环境中所有的Java进程;如果指定了hostid,它就会显示指定hostid上面的java进程,不过这需要远程服务上开启了jstatd服务,可以参看前面的jstatd章节来启动jstad服务。
命令格式 :jps [ options ] [ hostid ]
参数说明 :
-q 忽略输出的类名、Jar名以及传递给main方法的参数,只输出pid。
-m 输出传递给main方法的参数,如果是内嵌的JVM则输出为null。
-l 输出应用程序主类的完整包名,或者是应用程序JAR文件的完整路径。
-v 输出传给JVM的参数。
-V 输出通过标记的文件传递给JVM的参数(.hotspotrc文件,或者是通过参数-XX:Flags=<filename>指定的文件)。
-J 用于传递jvm选项到由javac调用的java加载器中,例如,“-J-Xms48m”将把启动内存设置为48M,使用-J选项可以非常方便的向基于Java的开发的底层虚拟机应用程序传递参数。下面样例均在linux的jdk1.7下测试
2、jstat
Jstat用于监控基于HotSpot的JVM,对其堆的使用情况进行实时的命令行的统计,使用jstat我们可以对指定的JVM做如下监控:
- 类的加载及卸载情况
- 查看新生代、老生代及持久代的容量及使用情况
- 查看新生代、老生代及持久代的垃圾收集情况,包括垃圾回收的次数及垃圾回收所占用的时间
- 查看新生代中Eden区及Survior区中容量及分配情况等
jstat工具特别强大,它有众多的可选项,通过提供多种不同的监控维度,使我们可以从不同的维度来了解到当前JVM堆的使用情况。详细查看堆内各个部分的使用量,使用的时候必须加上待统计的Java进程号,可选的不同维度参数以及可选的统计频率参数。
命令格式:jstat [ option vmid [interval][s|ms][count]]
option 参数如下面表格
[class] 用于查看类加载情况的统计
interval 和count 代表查询次数和间隔。
使用样例:
[root@tools138 ~]# jstat -class 2897
Loaded Bytes Unloaded Bytes Time
67431 113866.2 59850 98607.5 1884.07
[root@tools138 ~]# jstat -compiler 2897
Compiled Failed Invalid Time FailedType FailedMethod
3782 1 0 507.88 1
org/apache/tomcat/util/IntrospectionUtils setProperty
jstat -gc 2897 100 10
表示查询系统进程为2897的java程序gc,每100毫秒查询一次,一共查询十次,显示结果每列的含义如下:
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
56832.0 57344.0 42356.2 0.0 235008.0 8153.1 699392.0 629319.5 119296.0 54057.7 66 3.869 3 1.772 5.642
S0C 新生代中Survivor space中S0当前容量的大小(KB)
S1U 新生代中Survivor space中S1容量使用的大小(KB)
EU Eden space容量使用的大小(KB)
OC Old space当前容量的大小(KB)
PU Permanent space使用容量的大小(KB)
YGC 从应用程序启动到采样时发生 Young GC 的次数
YGCT 从应用程序启动到采样时 Young GC 所用的时间(秒)
FGC 从应用程序启动到采样时发生 Full GC 的次数
FGCT 从应用程序启动到采样时 Full GC 所用的时间(秒)
GCTT 从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC
3、jinfo
jinfo可以输出并修改运行时的java 进程的opts。用处比较简单,用于输出JAVA系统参数及命令行参数。
命令格式:jinfo [option] pid
使用样例:
[root@tools138 ~]# jinfo -flag MaxNewSize 2897
-XX:MaxNewSize=18446744073709486080
4、jmap
jmap用于生成堆转储快照(一般称为heapdump或者dump文件)。当然也可其他方法比如加参数-XX:+HeapDumpOnOutOfMemoryError参数,在虚拟机OOM异常的之后自动生成dump文件,也可以通过-XX:+HeapDumpOnCtrlBreak参数则可以使用Ctrl+Break键让虚拟机生成dump文件。在前文《 JAVA虚拟机之3:CMS垃圾收集器》测试中就有生成。dump文件生成后可借助jha、MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer来对dump分析。jmap不仅能获取dump还可以查询finalize执行队列,java堆和永久代详细信息,空间使用率,当前用的是什么收集器等。
jmap -J-d64 -heap pid
命令格式:jmap [ option ] pid
参数说明:
-dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件
-finalizerinfo 打印正等候回收的对象的信息
-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况
-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量
-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来
-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效
使用样例:
[root@tools138 ~]# jmap -dump:format=b,file=eclipse.bin 2897
Dumping heap to /root/eclipse.bin ...
Heap dump file created
6、jstack
jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式:
如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。