JAVA虚拟机—JVM

        前言:JVM虚拟机是java程序运行平台,对于一个java程序是十分重要的。在运行时JVM虚拟机的类加载器将.class文件加载到虚拟机中转化为虚拟机的可运行文件运行,在现在的虚拟机中提供了一个HotSpot(热点探测)机制,频繁读取达到某个阈值的.class文件,会直接被以虚拟机可运行的文件存放在虚拟机中。JVM虚拟机中有两个可选机制版本client和server,clident机制为默认机制,如想更改JVM机制版本在jdk\jre\lib\i386\jvm.cfg文件中更改,将server KNOWN放在client KNOWN上即可。client机制一般运行于客户端应用,消耗较少的计算机资源;server机制一般用于服务端应用,消耗较多的计算机资源。

一:JVM的结构

1:JVM结构组成

(1)类加载子系统与方法区:类记载子系统负责从文件系统或网络中加载class信息,加载的类信息放在方法区中,方法区还会存放运行时的常量池信息,包括字符串和数字常量

(2)JAVA堆:java堆在虚拟机启动时建立,他是java程序最主要的内存工作区域,几乎所有的对象实例都存放在堆中。堆是所有线程共享的

(3)直接内存:java的NIO库允许java程序直接使用内存。直接内存是JAVA堆外的。直接向系统申请的内存空间。访问直接内存的速度一般会高于java堆。直接内存的大小不受JVM的限制,只要不高于物理内存就行。

(4)垃圾回收系统:垃圾回收系统是java虚拟机的重要组成部分,垃圾回收系统可以对方法区,java堆和直接内存进行回收。其中,java堆是垃圾收集器的工作重点。java的垃圾回收系统不同于C++,自动在后台工作,不需要手动控制。

(5)JAVA栈:每个java虚拟机线程都有一个私有的栈,一个线程的java栈在线程被创建时创建,java栈中保存着局部变量,方法参数,同时和java方法的调用,返回密切相关

(6)本地方法栈:本地方法栈用于本地方法的调用,java虚拟机允许java程序调用系统本地系统的本地方法。其他的和java栈类似。

(7)PC寄存器:每个线程的私有空间。在任何时刻,一个线程总在执行一个方法。这个正在被执行的方法称为当前方法。如果当前方法不是本地方法,PC寄存器就会指向当前正在被执行的指令。如果当前方法是本地方法,那么PC寄存器指向undefined

(8)执行引擎:执行引擎是JVM最核心的组件之一,它负责执行虚拟机的字节码,现代虚拟机为了提高执行效率,会使用即时编译技术将方法编译为机器码后执行。

2:JAVA堆的分代

(1)为什么要分代

       堆内存是java虚拟机管理内存中最大的一块,也是垃圾回收系统回收最频繁的一段。为了方便垃圾回收和内存管理,引入了堆的分代,堆的分代以对象的生存时间为标准,将堆分为了新生代(年轻代),老年代和永久代。这样在一定程度上提高了垃圾回收的效率,而且尽可能减少了内存碎片的产生

(2)内存分代划分

       java虚拟机将堆内存分为新生代(年轻代),老年代,永久代,永久代是HotSpot的特有概念,它采用永久代的方式实现方法区。而HotSpot也有去永久化的趋势,jdk1.7已经将字符串常量池从永久代移除。永久代主要存放常量,类信息,静态变量等数据,与垃圾回收关系不大。新生代和老年代是垃圾回收的主要区域,垃圾回收时以新生代和老年代之间为分界线使用不同的垃圾回收器,内存分代示意图如下:

2-1:新生代(年轻代)

       新生成的对象优先存放于新生代中,新生对象朝生夕死,存活率很低。常规进行一次垃圾回收,新生代会回收70%-95%的空间,回收效率很高

       HotSpot将新生代划分为三块,一块较大的Eden区和两块较小的Survivor区(from区和to区),from有时被称为S0区,to有时被称为S1区,默认的比例为8:1:1。划分的目的是HotSpot以复制算法回收新生代。新创建的对象会保存在Eent中,大对象直接进入老年代。当Eent没有足够的空间进行分配时,虚拟机发起一次Minor GC,GC开始时候,to区域中是空的(每次都是),GC开始时,Eent区域中存活下来的对象会被复制到to中,from中对象的年龄值达到年龄阈值的(通常是15,对象每逃过一次GC,年龄值+1,GC分代年龄存放在对象的Header中),被复制到老年代中,没有达到阈值的被复制到to区中,然后清空Eden区和to区,from区和to区交换身份。现在to区便又为空,为下次GC提供空间。

2-2:老年代

       在新生代中经历了多次GC后存活下来的对象会保存到老年代。老年代中的对象的生命周期较长,存活率比较高,在老年代中进行GC的频率相对较低,而且回收的速度也比较慢。

2-3:永久代

       永久代存放类信息,常量,静态变量,即时编译器编译后的代码等数据没,对这一区域而言,一般而言不进行垃圾回收

二:JVM垃圾回收算法和收集器

1:垃圾回收常见算法

(1):引用计数

       比较古老的回收算法,原理是此对象有一个引用,增加一个计数,删除一个引用则减少一个计数。垃圾回收时,回收计数为零的对象,此算法的缺点是无法处理循环引用问题

(2):复制

       此算法把内存空间划分为两个相等的区域,每次只使用其中一个区域。垃圾回收时将正在使用的对象复制到另一个区域。此算法只处理正在使用的对象,因此复制成本低,而且复制时可以进行内存整理,不会出现内存碎片。缺点是需要两倍的内存空间

(3):标记-清除

      此算法执行分两阶段,第一阶段从引用根节点开始标记所有被引用的对象,第二次遍历整个堆,把未标记对象的清除,此算法需要暂停整个应用,而且会产生内存碎片

(4):标记-整理    

     此算法执行分两阶段,第一阶段从引用根节点开始标记所有被引用的对象,第二次遍历整个堆,把未标记对象的清除,并吧存活对象压缩到堆的某一部分,此算法不会产生碎片,也不会浪费空间,结合了2,3的优点。

2:JVM中垃圾收集器

(1):Scavenge GC(次收集)和Full GC(全收集)的区别

       新生代 GC(Scavenge GC || Minor GC):作用于新生代的GC,运行十分频繁,回收速度也比较快,当Eden空间不足以为对象分配空间时,会触发Scavenge GC。一般当Eent区为对象分配空间失败时,会触发Scavenge GC,对Eent区进行GC,清除非存活对象,将存活对象保存到Survivor区。然后整理Survivor两个区。Eent区的GC非常频繁,所以采用速度快,效率高的算法。

       老年代 GC(Full GC || Major GC):发生在老年代的GC,GC时至少伴随一次Minor GC,Full GC比Minor GC慢十倍以上。当老年代内存不足,或调用System.gc()方法时,会触发Full GC。

次收集:

当新生带对堆空间紧张时会被触发,收集间隔较短

全收集:

老年堆或持久代空间满时会被触发,也可以使用System.gc()方法来启动

3:分代垃圾收集器

3-1:新生代(年轻代)收集器

(1)串行收集器(Serial)

       Serial收集器是HotSpot运行在Client模式下的默认新生代收集器。使用复制算法,他的特点是只用以一个CPU/一条线程去完成工作,且在GC时必须要暂停其他所有的工作线程(STW,可以使用-XX:+UseSerialGC打开)。这一点通过安全点的触发和释放完成

(2)并行收集器(ParNew)

      ParNew是前面Serial的多线程版本,除使用多条线程进行GC外,其他的控制参数,收集算法,对象分配规则,回收策略等和Serial完全相同(也是使用VM启用老年代CMS收集器-XX:useConMarkSweepGC的默认新生代收集器,useConMarkSweepGC收集器也可以与Serial收集器搭配使用),在单核处理器下的效率不一定高于单线程的Serial,而且多线程切换麻烦,所以可以通过XX:ParallelGCThreads=<N>参数控制参与GC的线程数量。减少了安全点的时间,即减少了用户线程等待的时间,从理论上垃圾收集线程可以与工作线程一同运行,但实际上并未完全实现这一点

(3)Parallel Scavenge收集器

      与ParNew类似,Parallel Scavenge也是使用复制算法,也是并行多线程收集器,但是与其他收集器尽可能缩短垃圾收集时间不同,Parallet Scavenge收集器更关注系统吞吐量(系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。停顿时间越短越适用于用户交互的程序,良好的响应速度可以提高用户体验,而高吞吐量则适用于后台运算而不需要太多交互的任务,最高效率利用CPU时间。

Parallel Scavenge提供了如下参数设置系统吞吐量

Parallel Scavenge收集器参数 描述
-XX:MaxGCPauseMillis (毫秒数)收集器将尽量保证内存回收时间不超过设定值,但如果太小会导致GC的频率增加
-XX:GCTimeRatio (整数:0<GCTimeRatio<100)是垃圾收集时间点

3-2:老年代收集器

(1)Serial Old收集器

      Serial Old是Serial收集器的老年代版本,同样是单线程收集器,使用“标记-整理”算法(老年代没有Survivor区,无法使用复制算法)。可以与任何一个新生代收集器配合使用。

(2)Parallel Old 收集器

       Parallel Old是Parallel Scavenge的老年代版本,使用多线程和“标记-整理”算法,吞吐量优先,主要与Parallel Scavenge配合使用。

(3)CMS收集器

       CMS(Concurrent Mark Sweep)收集器是一款划时代的收集器,一枚真正意义上的并发收集器(不用暂停用户线程,用户线程和GC线程并行,称为并发)。虽然现在有理论上表现更好的G1收集器,但主流的互联网企业线上选用的仍然是CMS。CMS是一种以获取最短回收停顿时间为目标的收集器,CMS又称为多并发低暂停的收集器,基于“标记-清除”算法(CMS可以与Serial,ParNew新生代收集器配合使用,默认使用ParNew)大概分为以下四个步骤:1),3)需要暂停工作线程

1):初始标记(initlal mark)

2):并发标记(CMS Concurrent Mark:GC rootsTracing过程)

3):重新标记(CMS remark)

4):并发清除(CMS concurrent Sweep,死亡对象被就地释放。注意:此处没有压缩)

3-3:分区收集器-G1收集器

       G1收集器是面向服务器端的收集器,主要目的是配备多核CPU的服务器治理大内存,G1收集器保留老年代,新生代,永久代的概念,但是不基于分代处理,而是将堆分为多个大小相等的独立区域。可用于整个堆的垃圾回收。
--xx:+UseG1GC  启用G1收集器

三:JVM的调优

1:JVM自带的监测小工具

(1)jps

用于查看JVM中所有进程的具体状态,包括进程的PID,程序路径等

jps -v   //输出JVM参数

jps -q   //输出JVM中各个线程的PID

jps -m   //输出main方法的参数

jps -l    //输出完全的包名,主类名,PID等

(2)jstat 

监测资源和性能,监测JVM内存中各个区域的内存大小和使用量

结构:jstat [option]  [pid]  [time>]  [count]

option  //可选参数

time  //间隔多长时间打印一次,单位毫秒

count  //打印多少次,不写无限打印

option:

jstat -compiler [pid]   //显示JVM实时编译的数量等信息

jstat -class [pid]   //显示加载类的数量,和所占用的内存空间

jstat -gc [pid]   //显示GC的信息,查看各区的分配大小和使用量

jstat -gcutil [pid]   //统计GC信息

jstar  -gccapacity [pid]  //JVM中三代对象的内存使用和占用状态

jstat -printcompilation [pid]  //当前JVM执行的信息

(3)jmap

可以获得JVM的堆中的详细信息,从而分析堆的运行状态

结构:

jmap [option] [pid]

参数:

jmap -heap [pid]   //打印堆的信息。GC算法,内存大小和使用情况等

jmap -histo [pid]   //打印堆的对象信息,包括内存大小等

jmap -histo:live  [pid]   //会先触发GC

jmap -finalizerinfo   //打印等待回收的对象信息

(4)jstack

打印栈的详细信息,从而分析栈的运行状态

结构:jstack [option] [pid]

参数

jstack -F [pid]   //当jstack -l [pid]没有响应时强制打印栈信息

jstack -l [pid]   //长列表,打印锁的附加信息

jstack -h  //打印帮助信息

(5)jconsole

监控java应用程序

jconsole [pid]  //本地监控

jconsole [IP:port]   //远程监控   

远程监控需要配置jmx代理信息,如修改tomcat的bin目录下吃的catalina.bat文件

set JAVA_OPTS= %JAVA_OPTS% -Djava.rmi.server.hostname=HostIP
set JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.port=8888
set JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.ssl=false
set JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.authenticate=false

(6)jvisualvm

jdk自带的图形话监测工具,可以使用插件,如Visual GC(专门用于监测垃圾回收系统的插件)

2:JVM参数介绍

参数的使用环境(多个参数用空格隔开):

(1)可以在eclipse中设置参数

 run as->run configurations->选择运行的程序->arguments->VM arguments->run

(2)命令行中设置参数

(3)基于JVM的程序自带参数设置,如Tomcat

 Tomcat默认可以使用的内存为128MB。

        Windows下,在文件/bin/catalina.bat中,在rem Guess CATALINA_HOME if not definedset CURRENT_DIR=%cd%后面添加JVM参数

        Unix下,在文件/bin/catalina.sh中,在文件头部设置JVM参数

配置大概如下:

JAVA_OPTS="-Xms50m -Xmx100m -XX:ParallelGCThreads=8"

2:一些参数

-Xms //初始堆大小,如-Xms10m

-Xmx  //最大堆大小,如-Xmx100m

-XX:NewSize=n  //设置新生代大小

-XX:MaxPermSize=n  //设置持久代大小

-XX:NewRatio=n   //年轻代和老年代的比值

-XX:SurvivorRatio=n   //Eden区和Survivor区的比值

XX:+UseSerialGC   //设置串行收集器

-XX:+UseParallelGC  //设置并行收集器

-XX:+UseParalledlOldGC   //设置并行年老代收集器

-XX:+UseConcMarkSweepGC   //设置并发收集器

等等等等。。。。。

官方英文文档:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

非官网中文文档:http://www.jvmer.com/jvm-xx-%E5%8F%82%E6%95%B0%E4%BB%8B%E7%BB%8D/

猜你喜欢

转载自blog.csdn.net/qq_36297434/article/details/83863050