Java的JVM,可能学Java的都知道这个名字,博客或者百度也有一大堆,因为面试原因,大致也能说得上一些东西,今天重新梳理一下,一方面复习,一方面加深理解
JVM可以理解成一个虚构出来的计算机,一个特点是跨平台型,将源码编译成目标代码,这个目标代码就是字节码(也就是Java里面的*.class文件),在任何平台上,windows、linux、类unix、tru64等机器上,只要安装对应的jdk,即可实现在一个平台上编译,不需要其他的修改。
Java的类型分为两大类:基础数据类型、非基础数据类型
基础数据类型:
byte: 1字节
short: 2字节
int: 4字节
long: 8字节
float: 4字节单精度浮点数
double: 8字节双精度浮点数
char: 2字节无符号Unicode字符
非基础数据类型(其他数据类型):
object:对象,所有的自己编写的类都是对象:占用4个字节,在Java虚拟机中的堆中存在(后面会深入讲到)
通过调用jre来进行将Java源码编译成*.class文件
堆:是一个运行时数据区,类的实例(对象)和其他数组从中分配空间,它的管理是由垃圾回收来负责的:不给程序员显式释放对象的能力。Java不规定具体使用的垃圾回收算法,可以根据系统的需求使用各种各样的算法。
(主要用于存放对象,存取速度慢,可以运行时动态分配内存,生存期不需要提前确定)
栈:一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。
(主要用来执行程序,存取速度快,大小和生存期必须确定,缺乏灵活性)
堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用 new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!
总结:
1、GC不会立即收集这个我们都知道
2、GC主要是回收堆和方法区的内存
问题:
1.什么是新生代(Young),老年代(Old),永久代(Perm)?
简单的说:
GC会分区域回收垃圾,为了方便对内存进行管理,分为新生代、老年代和永久代
新生代就是:频繁收集的区域就是新生代
老年代:收集频率较少的区域
永久带:基本上不回收的区域成为永久代
堆=新生代+老年代
2.新生代、老年代、永久代各保存哪些信息?jvm是怎么划分
新生代:新生代划分三个区域,Eden、SurvivorA和SurvivorB,当对象第一次创建的时候,都是存放在新生代中的Eden中,当Eden没有空间的时候,会执行一次GC,如果存在某些对象不能引用,会把这些对象复制到SurvivorA或者SurvivorB中的任意一个。但是肯定有一个Survivor是空的,当Survivor里面的对象超过一定回收次数的阀值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)),这部分对象会被转移到老年代
备注:
1)可以根据程序需要,增加设置多个Survivor区域,减少对象被放入到老年代
2)GC回收的时候程序会挂起,也就是这个时候程序会很”卡”,jvm调优应该尽量降低GC的时间,减少FULL GC的次数
老年代:当新生代不能回收的对象,就保存在老年代中,在老年代回收的频率会比较低
永久代:存在静态文件,静态的东西,几乎不会被GC
3.Minor GC、Major GC、Full GC到底有什么区别?
Minor GC:就是对堆中的新生代进行一次GC
Major GC:对堆中老年代进行一次GC,速度一般比Minor GC慢10倍
Full GC:全部回收,对堆中的新生代和老年代都进行GC,并且也会栈执行GC,比Major GC更慢
4.Full GC是对堆全部GC,那么会挂起时间比较长,Minor GC(新生代空间满)和Major GC(一定的年龄阀值)都有一定的策略,那么什么情况下会进行Full GC呢?1)程序中显示的调用System.gc()。当程序员在程序中调用System.gc()的时候,会产生full gc,应该禁止显示调用System.gc(),让虚拟机自己去管理它的内存,如果程序中有RMI接口或者,应该禁止远程RMI调用GC,可通过通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc
2)老年代空间不足
当老年代空间不足的时候,会产生Full GC。但是当GC后仍然空间不足的话,会产生
Java.lang.OutOfMemoryError: Java heap space
所以在jvm调优中应该尽量让对象在Major GC中被回收,以及不要创建过大的数组和对象
3)永久代空间不足
当永久代空间不足,并且没有配置CMS GC的时候也会执行Full GC,当Full GC回收后,仍然不够空间,会产生
java.lang.OutOfMemoryError: PermGen space
JVM参数:
堆设置
-Xms :初始堆大小
-Xmx :最大堆大小
-XX:NewSize=n :设置年轻代大小,设为整个堆大小的1/3或者1/4,和XX:MaxNewSize两个值设为一样大。
-XX:NewRatio=n: 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n :年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n :设置持久代大小
收集器设置
-XX:+UseSerialGC :设置串行收集器
-XX:+UseParallelGC :设置并行收集器
-XX:+UseParalledlOldGC :设置并行年老代收集器
-XX:+UseConcMarkSweepGC :设置并发收集器
垃圾回收统计信息
-XX:+PrintHeapAtGC GC的heap详情
-XX:+PrintGCDetails GC详情
-XX:+PrintGCTimeStamps 打印GC时间信息
-XX:+PrintTenuringDistribution 打印年龄信息等
-XX:+HandlePromotionFailure 老年代分配担保(true or false)
并行收集器设置
-XX:ParallelGCThreads=n :设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n :设置并行收集最大暂停时间
-XX:GCTimeRatio=n :设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode :设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n :设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
JVM的任何一个参数都不是越大越好,也都不是越小越好,通过设置jvm的参数,我们要达到三个目的:
1)GC的时间足够的小
2)GC的次数足够的少
3)发生Full GC的周期足够的长
1和2是想反的,我们设想,要想GC时间少,那么堆肯定要设置小一点,但是要保证GC时间足够的少,我们又要保证堆空间大一点,所以,我们必须根据实际情况平衡
(1)针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值
(2)年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
(3)在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC ,默认为Serial收集
(4)线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。
整理:
1、多数的Java应用不需要在服务器上进行GC优化;
2、多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;
3、在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);
4、减少创建对象的数量;
5、减少使用全局变量和大对象;
6、GC优化是到最后不得已才采用的手段;
7、在实际使用中,分析GC情况优化代码比优化GC参数要多得多;
1)先确定tomcat或者jetty等运用服务器的pid
windows:使用netstat -ano |findstr “8080”(假如服务器端口是8080)
linux:linux使用ps aux|grep “tomcat”命令(根据实际情况修改)
2)上面我在windows上面已经确定了pid是6396,那么在命令行输入:
jstat -gcutil 6396 1000 5表示监控6396端口,每1000毫秒执行一次打印,总共打印五次
Options — 选项,我们一般使用 -gcutil 查看gc情况
vmid — VM的进程号,即当前运行的java进程号
interval– 间隔时间,单位为秒或者毫秒
count — 打印次数,如果缺省则打印无数次
如图:
S0 — Heap上的 Survivor space 0 区已使用空间的百分比
S1 — Heap上的 Survivor space 1 区已使用空间的百分比
E — Heap上的 Eden space 区已使用空间的百分比
O — Heap上的 Old space 区已使用空间的百分比
P — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
我们可以对每个参数进行分析
在windows目录有C:\Program Files\Java\jdk1.7.0_55\bin\jconsole.exe