1.Jvm内存模型结构
2.堆
堆是java虚拟机启动的时候建立的,堆中几乎存放了所有Java实例对象的信息,堆空间是所有线程共享的区域,这是与Java内存密切相关的区域。
堆中分为老年代(old)和新生代(young),其中新生代包括 Eden,S0(from),S1(to),S0和S1是两块相等的区域,刚创建的对象会先进入到新生代的Eden区,在进行一次新生代回收(minor GC)后,存活下来的对象会进入到S0或者S1中,每经过一次minor GC该对象的年龄就会+1,当该对象到达一定年龄后就会进入老年代,如图展示堆的结构
3.方法区
和堆一样是线程共享的一块区域,它用于保存系统的类信息,类的字段,类的方法,常量池等,方法区的大小决定了可以保存多少类,如果系统定义了太多的类,导致方法区溢出,同样系统也会抛出内存溢出的错误,在JDK1.6,JDK1.7中方法区可以理解为永久区(perm),在JDK1.8之后改为元数据区,元数据区和永久区不同,元数据区使用的是堆外的直接内存,如果不指定元数据区的大小(-XX:+MaxMetaspaceSize),它会耗尽系统的内存.如果元数据区溢出同样会发生内存溢出错误。
4.直接内存
Java的NIO(New IO)库处于堆外,用的是直接内存,不受Xmx大小的限制,通常访问直接内存会比java堆快,处于性能考虑,读写频繁的场合使用直接内存会优于Java堆,虽然直接内存不受Java堆大小的限制,但是堆和直接内存都受限于系统内存的最大大小
5.垃圾回收系统
垃圾回收系统可以对直接内存,方法区和堆进行回收,其中堆是垃圾回收系统的主要工作重点,标识并释放出无用的对象
6.栈
每个虚拟机都有一个私有的栈,一个线程的Java栈在线程创建的时候被创建,java栈和数据结构中的栈有着类似的含义,Java栈只包含两种操作(入栈和出栈),Java栈中保存着栈帧,还有局部变量,方法参数,其中和方法的返回,调用密切相关。栈帧中包含有局部变量表,操作数栈,帧数据区。
局部变量表
局部变量表中包含有很多的槽位,槽位之间是可以复用的
操作数栈
操作数栈保存着计算过程的中间结果,同时作为计算过程中临时变量的储存空间,操作数栈也是一个先进后出的数据结构,只支持出栈和进栈两种操作。例如自增的指令,在操作数栈中会创建两个整数并行加法计算,计算结果会被入栈。
帧数据区
帧数据区用来支持常量池的解析,正常方法返回和异常处理,在帧数据区中保存着用来访问常量池的指针,方便程序访问常量池
7.本地方法栈
本地方法栈与栈类似,最大的不同是Java栈用与Java方法,本地方法栈用于本地方法的调用(一般用C编写
8.程序计数器(PC寄存器,program counter)
PC寄存器也是每个线程私有的,Java虚拟机会为每个线程创建一个PC寄存器,,在任意时刻,Java线程总是在执行一个方法,这个方法是当前方法,PC寄存器会指向这个正在执行的方法,如果这个方法是本地方法,PC寄存器的值则为undefined
9.执行引擎
执行引擎是Java虚拟机的核心组件之一,它负责执行虚拟机的字节码。也就是将虚拟机中的方法编译成机器码后再执行
10.通过示例来描述堆,方法区,栈之间的关系
public class SimpleHeap {
private int num;
public SimpleHeap(int num){
this.num = num;
}
#方法
public void show(){
System.out.println("num"+num);
}
public static void main(String[] args) {
#s1是(局部变量),new SimpleHeap(1)是创建一个实例对象
SimpleHeap s1 = new SimpleHeap(1);
SimpleHeap s2 = new SimpleHeap(2);
#调用函数show()
s1.show();
s2.show();
}
}
此声明了一个SimpleHeap类,main函数中创建了两个实例对象(new SimpleHeap(1) ,new SimpleHeap(2) )和两个局部变量(s1,s2)及其方法的引用,首先我们会在堆中创建两个实例对象,在栈中创建两个局部变量,并且两个局部变量的地址指向堆中的两个实例对象,方法区中则存在类和其方法实现(show方法),如图
11,常用的jvm参数设置
内存参数 | 描述 | |
-Xmx | 堆的最大内存大小 | |
-Xms | 堆的初始化内存大小 | |
-Xmn | 新生代的大小 | |
-Xss | 虚拟机栈的大小 | |
-XX:SurvivorRatio=2 | 新生代中eden和from/to空间比例的关系含义-XX:SurvivorRatio=eden/from=eden/to=2 | |
-XX:NewRatio=2 | 老年代/新生代=2 | |
-XX:+HeapDumpOnOutOfMemoryError | 在内存溢出时可以导出整个堆信息 | |
-XX:+HeapDumpPath | 导出堆信息的文件路径 | |
-XX:PermSize | JDK1.7之前用永久区的大小 | |
-XX:MaxMetaspaceSize | JDK1.8之后用元数据区的大小 | |
-XX:MaxDirectMemorySize | 直接内存大小,若不设置,默认为Xmx(堆最大内存)大小, | |
-XX:MaxTenuringThreshold | 该参数是新生代经过15次回收后仍然存在的对象,必然会晋升到老年代,默认为15。未达到该次数的对象也有可能会晋升 | |
打印参数 | 描述 | |
-XX:+PrintGC | 打印GC的信息 | |
-XX:+PrintGCDetails | 打印GC的详细信息 | |
-XX:+PrintHeapAtGC | 打印GC对堆的详细信息 | |
-XX:+PrintGCTimeStamps | 打印GC的同时输出GC的时间参数 | |
-XX:+PrintGCApplicationConcurrentTime | 打印应用程序的执行时间 | |
-XX:+PrintGCApplicationStopedTime | 打印应用程序由于GC而产生的停顿时间 | |
-XX:+PrintReferenceGC | 跟踪软引用、弱引用、虚引用、和Finalize队列 | |
-Xloggc:log/gc.log | 不在控制台中输出GC日志,在log文件下的gc.log记录GC的日志信息 | |
-XX:+TraceClassLoading | 跟踪类的加载 | |
-XX:+TraceClassUnloading | 跟踪类的卸载 | |
-XX:+PrintClassHistogram | 查看系统中类的分布情况 | |
-XX:+PrintVMOptions | 打印虚拟机中的显式参数 | |
-XX:+PrintCommandLineFlags | 打印虚拟机中的显式和隐式参数,隐式参数未必是通过命令行给出,它可能由虚拟机启动时自行设置的 | |
-XX:+PrintFlagsFinal | 打印出系统的所有参数,需要学习的可以参考参数学习 大概500多行 | |
jvm运行模式的参数 | 描述 | |
-Client | 客户端运行(启动快,对于登录系统比较使用) | |
-Server | 服务端运行(对系统参数做优化启动,启动慢,服务运行快,对于长期运行的系统用server启动会对系统整体性能上有不小的帮助) | |
垃圾回收器参数 | 描述 | 算法 |
-XX:+UseSerialGC | 串行回收器,-Client模式下默认使用垃圾回收器 | 在老年代用的是标记压缩算法 |
-XX:+UseParNewGC(多线程,独占式) | ParNew为并行回收器,启用多个线程(-XX:+ParallelGCThreads指定工作线程数)进行垃圾回收,新生代使用ParNew回收器,老年代使用串行回收器 | 复制算法 |
-XX:+UseParallelGC(多线程,独占式) | 新生代使用ParallelGC回收器(和ParNewGC回收器差不多,但是Parallel能控制系统的吞吐量,主要用两个参数控制-XX:MaxGCPauseMillis和-XX:GCTimeRatio),老年代使用串行回收器 | 复制算法 |
-XX:+UseConcMarkSweepGC(多线程) | 新生代使用ParNew回收器,老年代使用CMS回收器(CMS主要关注于停顿时间STW),CMS默认启动的线程数(ParallelGCThreads+3)/4. | 标记压缩算法 |
-XX:+UseParaelOldGC(多线程,独占式) | 新生代使用ParallelGC回收器,老年代使用ParallelOldGC回收器,和ParallelGC功能一样,只是用来设置老年的的回收器 | 标记压缩算法 |
-XX:+UseG1GC(多线程) | 打开G1回收器(和CMS类似,不同的是,CMS需要隔段时间进行内存碎片整理,对系统停顿时间有影响) | 分区算法 |
-XX:+ParallelGCThreads | 指定ParNew回收器的工作线程数,最好和CPU数量相当,默认情况下,当Cpu的数量小于8时,ParallelGCThreads的值等于CPU的数量,当CPU的数量大于8是,ParallelGCThreads的值等于3+(5*CPU_Count)/8 | |
-XX:MaxGCPauseMillis | 设置最大垃圾收集停顿时间,是一个大于0的整数,ParallelGC在工作时,会调整JAVA堆大小或者其他一些参数,会尽可能的将停顿时间控制MaxGCPauseMillis内 | |
-XX:GCTimeRatio | 设置吞吐量大小,它的值是0到100之间的整数,例如,GCTimeRatio的值等于19(默认值),则系统用于垃圾回收的时间不超过1/(1+19)=5%,默认情况下,它的取值是99,即不超过1/(1+99)=1%的时间用于垃圾回收 | |
-XX:+UseAdaptiveSizePolicy | ParNewGC和ParallelGC的另一个不同之处,就是ParallelGC可以使用-XX:+UseAdaptiveSizePolicy打开自适应GC策略,在手工调优比较困难的场合,可以使用该参数,仅需指定虚拟机的最大堆(Xmx),停顿时间(MaxGCPauseMillis),目标吞吐量(GCTimeRatio),让虚拟机自己完成调优工作 | |
-XX:+CMSPreclearningEnabled | CMS回收器中是否进行预清理操作,再正式清理前做准备,及控制一次停顿时间 | |
-XX:CMSInitiatingOccupancyFraction | 回收阙值,默认是68,即当老年代的使用率达到68%时就会进行CMS回收。因为CMS不是独占式的,即应用程序在不停的工作,在不断的产生垃圾,所以在CMS回收过程中还应保证系统有足够的内存空间存放新生的垃圾。在CMS过程中,已经出现了内存不足的情况,CMS就会回收失败,虚拟机将启动老年代串行回收器 | |
-XX:+UseCMSCompactAtFullCollection | 开关,使CMS在垃圾回收完后进行一次内存碎片整理 | |
-XX:CMSFullGCsBeforeCompaction | 设定进行多少次CMS回收后进行一次内存碎片压缩整理 | |
-XX:+CMSClassUnloadingEnabled | 开关,回收perm区中的class数据 | |
注意 | 并发是指回收器和应用程序线程交替执行。并行是指应用程序停止,同时由多个线程一起执行GC,因此并行回收器不是并发的,并行它需要应用程序完全挂起,不存在交替执行的步骤. | |
-XX:+DisableExplicitGC | 禁用手动GC(System.gc()函数无效) | |
-XX:+ExplicitGCInvokesConcurrent | System.gc()函数会使用默认的回收器,使G1和CMS回收器使用失效,该参数的作用是在使用System.gc()函数时让G1和CMS回收器能够正常使用 | |
-XX:-ScavengeBeforeFullGC | 每一个并行回收器,在进行FullGC前会触发一次新生代GC,该参数是是否要出发这一次新生代GC,默认为True |