JVM基本结构
句柄池
JDK8中把存放元数据中的永久内存从堆内存中移到了本地内存(native memory)中,这样永久内存就不再占用堆内存,它可以通过自动增长来避免JDK7以及前期版本中常见的永久内存错误(java.lang.OutOfMemoryError: PermGen)。
①类加载子系统:负责从文件系统或网络中加载class信息,存放在方法区中
②方法区:存放类信息和静态信息(常量信息、常量池信息、字符串常量和数字常量等)所有线程共享,可以理解为永久区(常量池是其中的一部分)
③Java堆:Java虚拟机在启动的时候创建Java堆,它是Java程序最主要的内存工作区域,所有的对象实例放在Java堆中,堆空间是所有线程共享的,一个JVM一个堆
④直接内存:Java的NIO允许Java程序直接使用内存,从而提高性能,直接内存速读高于Java堆,读写频繁时可以考虑使用
⑤Java栈:每个虚拟机线程都有私有的栈,一个线程的Java栈,在线程创建的时候被创建,Java栈中保存着局部变量、方法参数、Java方法的调用和返回值等,一个线程一个栈
⑥本地方法栈:线程私有,与Java栈很相似,最大的不同在于,本地方法栈用于本地方法调用,Java虚拟机栈允许Java直接调用本地方法(通常用C编写)
⑦垃圾回收系统:是Java的核心,也是必不可少的,Java有一套自己的垃圾回收机制
⑧PC寄存器:是每个线程私有的空间,Java虚拟机会为每个线程创建PC寄存器,存放当前执行环境指针,程序计数器,操作栈指针等
⑨执行引擎:核心组件,他负责执行虚拟机的字节码
堆、栈、方法区关系
堆:解决的是数据存储问题,怎么放,放在哪
栈:解决的是程序运行问题,程序如何执行,或者说如何处理数据
方法区:辅助堆栈的永久区,解决堆栈信息的产生,是先决条件
关系:一个User类;
类信息和静态信息存放在方法区中;
而实例引用和局部变量存放在栈中;
而实例对象放在堆中。
Java堆
线程间共享,Java堆是和Java应用程序关系最密切的内存空间,几乎所有的对象都放在其中,并且Java堆是自动化管理,通过垃圾回收机制,垃圾对象自动清理
Java堆结构图:
根据垃圾回收机制不同,Java堆有不同的结构,最常见的是将Java堆分为新生代和老年代
eden区:存放的是刚实例化出来的数据,当对象经历过一次GC的时候,就会将对象放在S0或者S1区
S0和S1:也称为form和to区,这两个区域大小相等,并可以互换角色,是由于复制算法
复制算法:其思想就是将内存空间分为两块,每次只是用其中一块(S0或者S1),在垃圾回收时,将正在使用的对象复制到S1,然后将S0的对象全部回收,反复去交换这两个内存角色,完成垃圾回收
tenured区:老年区,当对象每经历过一次GC,就会加一,到达一定阀值后(默认15),就会进入老年区
新生代和老年代都会经历GC回收,只是新生代频繁GC,而老年代不频繁GC
新生代与老年代的比例:1:2 0r 1:3
Java栈
Java栈是线程私有的内存空间(线程间独立),由三个部分组成:局部变量表、操作数栈、帧数据区
局部变量表:用于报错函数的参数及局部变量
操作数栈:主要保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间
帧数据区:保存至访问常量池的指针,还有当函数出现异常时,虚拟机需要的异常处理表
Java方法区
线程间共享,保存类信息,方法区大小决定了系统可以保存多少类,如果系统定义太多的类,会导致虚拟机抛出内存溢出异常,方法区可以理解为永久区
堆参数配置
垃圾回收及其算法
垃圾回收针对的是堆区
垃圾回收有很多算法:引用计数法、复制算法、标记压缩法,分代、分区思想
引用计数法:比较古老的垃圾收集算法,对象被引用+1,失去引用-1,弊端:无法处理循环引用
标记清除法:就是分为标记和清楚两个阶段,进行处理内存中的对象,弊端:空间碎片问题
复制算法(新生代):将内存分为两块,每次只是用其中一块,在垃圾回收时,将正在使用的对象复制到B内存区中,然后将A区的所有对象清楚,反复交换两个内存的角色,完成垃圾回收(Java中新生代的from和to空间就是使用的复制算法)
标记压缩法(老年代):是在标记清楚法基础上做了优化,把存活的对象压缩到内存的一段,而后进行垃圾清理
为什么新生代和老年代使用不同的算法?因为新生代GC回收的次数频繁,而老年代不频繁(因为老年代的对象比较稳定,所以需要回收的对象少,如果使用复制算法就浪费性能了)
垃圾回收时会暂时停顿
对象如何进入老年代
对象创建流程图
http://www.cnblogs.com/chenyangyao/p/5296807.html
JVM调优常用参数
- 堆设置
-Xms64m:初始化内存
-Xmx64m:最大内存
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值,1:2和1:3比较合理
-XX:MaxPermSize=n:设置持久代大小
-XX:MaxTenuringThreshold=15:新生代转换为老年代的阀值,默认15
-XX:PretenureSizeThreshold 指定对象超过一定的大小,就会直接晋级老年代,但是要注意TLAB区域优先分配空间,单位k
- 收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器,Parallel
-XX:+UseParalledlOldGC:设置并行年老代收集器,Parallel
-XX:+UseConcMarkSweepGC:设置并发收集器,CMS
-XX:ConcGCThreads:设置并发线程数量,CMS
-XX:+UseG1GC:使用G1回收器
- 并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置吞吐量大小,垃圾回收时间占程序运行时间的百分比。公式为1/(1+N)
-XX:UseAdaptiveSizePolicy 自动调整MaxGCPauseMillis、GCTimeRatio等
-XX:+UseParNewGC:新生代回收器=ParNew OR ParallelGC
- 并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:+ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
- 垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
- 查看JVM默认参数等信息
java -XX:+PrintFlagsInitial
http://blog.youkuaiyun.com/java2000_wl/article/details/8042010
注:一个Tomcat占用一个JVM进程;当应用没有人用时,和有人用时,系统的内存是不一样的,也就是说JVM会动态的向系统申请内存
JVM博客:
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
垃圾回收器
根据作用于不同的堆空间,分为新生代回收器和老年代收集器
串行垃圾收集器:单线程进行垃圾回收
并行垃圾收集器:多个线程进行垃圾回收
CMS回收器:并发标记清楚,标记清除法,关注系统停顿时间
ParalledlOldGC: 关注系统吞吐量
Java8默认:G1收集器,以前是CMS收集器
压测服务器吞吐量
JVM性能监控与故障处理工具
- JPS
JVM 进程状况工具
LVMID(vmid,虚拟机进程id)==PID(OS进程id)
jps -l :输出本机JVM的vmid(虚拟机进程id)和路径
jps -v:输出本机JVM的vmid(虚拟机进程id)和启动JVM时的参数
- jstat
JVM统计信息监视工具
jstat -gcutil vmid
E:Eden新生代使用率%
S0,S1:Survivor区使用率%
O:老年代使用率%
M:方法区使用率%
YGC,YGCT:YongGC,程序运行以来共发生MMinorGC 74次 耗时0.381秒
FGC,FGCT:FullGC,程序运行以来共发生FullGC 1次 耗时0.049秒
- jinfo
Java配置信息工具
- jmap
Java内存映像工具
jmap -heap vmid:显示Java堆详情信息
- JConsole
Java可视化监控
进入本地JDK目录
/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/bin
输入jconsole,然后在使用远程连接
- jvisualvm
强大的可视化 多合一故障处理工具
- jstack
查看Java栈和本地栈信息,也可以看线程的运行情况
jstack [ option ] pid
jstack和thread dump的结果相同
http://blog.youkuaiyun.com/xiao_287130/article/details/53607428