一、Java JVM组成1、简介: JVM、Java语言独有的垃圾回收处理机制。 个人汇总垃圾回收图例:
1.1由JVM组成的垃圾收集器分为堆栈、Heap、方法区域、PC注册程序计数器和本机方法堆栈
、Class Loader类加载器和本机接口组成。
1.2堆栈也称为堆栈内存,负责运行Java程序的是在创建线程时创建的,其生命周期是跟随线程的生命周期。 线程结束后,堆栈内存也被释放。 对堆栈来说没有垃圾回收问题,线程结束时该堆栈将溢出。 只要生命周期和线程匹配,线程就是专用的。 基本类型的变量和对象的引用变量被分配给函数的堆栈内存。
Java方法执行包含局部变量、操作数堆栈、动态链接和方法出口等信息的内存模型。
1、堆栈存储是什么?
堆栈帧主要存储三种类型的数据。 局部变量(Local Variables ) :输入和输出参数以及方法中的变量; 堆栈操作(操作堆栈) :堆栈,记录进入堆栈的操作; 堆栈数据(Frame Data ) :包括类文件、方法等。
2、叠运行原理
堆栈中的所有数据都以堆栈帧(Stack Frame )的形式存在,堆栈帧是存储器块、数据集、关于方法(Method )和运行时数据的数据集,以及方法a a方法调用b方法来推入堆栈帧F2,生成堆栈,于是堆栈帧F3也被推入堆栈,……执行完成后,首先推出F3堆栈帧,接着推出F2堆栈帧,接着是fff 3堆栈帧
堆栈溢出的原因
一)、是否有递归调用
二)、是否有大量循环或死亡循环
三)全局变量是否过多
四)、数组、列表、映射数据是否过大
堆栈类似于弹匣。 先进先出、8的数据类型、引用数据类型在堆栈上
常见异常的java.lang.stackOverFlowError、堆栈内存溢出和实例方法继续调用自己,导致溢出
1.2 Heap堆线程共享
JVM实例只有一个堆内存,并且可以调整堆内存的大小。 类加载器读取类文件后,必须将类、方法和常量变量放在堆内存中,并保存所有引用类型的真实信息,以方便执行器执行。 堆内存分为三个部分。
Young Generation Space普通长椅区Young
Tenure generation space养老区Old
Permanent Space永久商店Perm
堆模型
jdk1.7:
堆存储器的分布分为普通的长椅区、养老区和永久区。
其中常规长椅分为伊甸园区,生存0、生存1,因此新new对象放置在伊甸园区,当伊甸园空间存储达到70%时,jvm进行minor GC,清理伊甸园将剩余对象转移到生存0。 minor GC只发生在伊甸园区。
每次发生minor GC时,将剩下的对象依次向后移动。 也就是说,伊甸园区将在生存0到生存1、15次后到达养老区。 养老区空间不足时,会进行马焦格控制,也称为全格控制,全格控制只发生在养老区。 如果完全通用汽车运行不足以放置养老区中从前面移动过来的对象,则会报告错误。 Java.lang.out of memory error 3360 javaheapspace
如果发生Java.lang.out of memory error 3360 javaheapspace异常,则说明Java虚拟机的堆内存不足。 原因有两个:
)1) Java虚拟机堆内存设置不足,可以通过-Xms、-Xmx参数进行调整。
)在代码中创建了大量大对象,而且垃圾收集器没有收集这些对象的时间很长) )存在引用对象)。
堆栈溢出代码String str='www.niubi.com '; while(true ) str=strnew Random ).nextint ) 8888888 ) newrandom ).nextint (99999999 ); (7.2养老区
养老区用于存储从常规长椅区筛选出的JAVA对象,常见的游泳池对象活跃在该区域,很少发生垃圾回收
示例: byte [ ] b=new byte [ 1024 x 1024 x 1024 ];
永久空间在逻辑上是堆内存,实际上不是堆。 1.7前,他用于存储运行Java程序所需的jar包
Book b1=new Book (;
Book b1位于堆栈中,并与new Book (; 在山上的伊甸园里
1.8用元空间替换了永久区
永久空间非堆内存
永久空间—启动常规存储程序时需要加载的jar包
永久区域已满时,发生Java.lang.out of memory:permgenspace异常(永久内存溢出)
7.3永久区
永久存储区是存储JDK自身拥有的Class、Interface元数据的驻留内存区域。 这意味着它包含运行时所需的类信息,并且垃圾收集器不会回收加载到此区域的数据。 关闭JVM将释放此空间占用的内存。
如果ja出现了
va.lang.OutOfMemoryError: PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。或者大量动态反射生成的类不断被加载,最终导致Perm区被占满。
Jdk1.6及之前: 常量池分配在永久代
Jdk1.7: 有,但已经逐步“去永久代”
Jdk1.8及之后: 无
jdk1.7
jdk1.8
JDK 1.8之后将最初的永久代取消了,由元空间取代。
目的:将HotSpot与JRockit两个虚拟机标准
1.3 Method Area 方法区
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此区属于共享区间。 静态变量+常量+类信息+运行时常量池存在方法区中,实例变量存在堆内存中
1.4 PC Register 程序计数器每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。
1.5 Native Method Stack 本地方法栈线程私有的,它的具体做法是 Native Method Stack中登记native方法,在Execution Engine 执行时加载native libraies。
1.6 Native Interface 本地接口本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++程序,Java 诞生的时候是 C/C++横行的时候,要想立足,必须有调用 C/C++程序,于是就在内存中专门开辟了一块区域处理标记为 native的代码,它的具体做法是 Native Method Stack中登记 native方法,在Execution Engine 执行时加载native libraies。 目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过 Java程序驱动打印机,或者 Java系统管理生产设备,在企业级应用中已经比较少见。 因为现在的异构领域间的通信很发达,比如可以使用 Socket 通信,也可以使用 Web Service等等,不多做介绍。
1.7 Class Loader类加载器负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定
二、JVM垃圾回收器及调优 1、垃圾回收器结构图
1、Serial串行回收,2、Parallel并行回收3、CMS并发标记清除,4、G1,java10以前的
垃圾回收器
串行Serial
到达峰值,启动gc线程,清除继续运行,程序–>GC–>程序
多个垃圾回收线程,程序–>多个GC–>程序
不会完全暂停,并发更为复杂,但是会占用应用cpu
默认使用的是并行垃圾回收
串行并行
7种gc,串行gc(平常的板凳区),并行gc(平常的板凳区),ParNewGc(平常的板凳区),ParallelGC(平常的板凳区),ParalleloldGC(养老区)CMSGC(并发标记清除GC)(养老区)
垃圾回收器具体实现GC算法并实现内存回收
古老稳定,效率高,一个线程回收,会暂停其他的线程
-XX:+UseSerialGC,开启,开启后会使用:Serial(平常的板凳区)+Serial Old(Old区)的收集器组合
表示:平常的板凳代、老年代都会使用串行回收收集器,平常的板凳代使用复制算法,老年代使用标记整理算法
初始空间 最大空间 打印参数细节 查看默认使用GC
-Xmx10M -Xmx10m
-XX:+PrintGCDetails
-XX:+PrintCommandLineFlags
-XX:+UseSerialGC
并行收集器,多个线程进行垃圾回收,也会暂停线程,平常的板凳代并行多线程配合老年代的CMSGC工作
开启参数:-XX:+UseParNewGC 开启后使用ParNew+Serial Old的收集器(不再推荐),
平常的板凳代使用复制,老年代使用标记整理
-Xmx10M -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC
-XX:ParallelGCThreads
限制线程数,默认开启CPU数目相同的线程数
ParallelScavenge 并行回收gc算法,默认的
和老年代一样开启并行回收,吞吐量优先收集器
可控吞吐量,运行用户代码时间/(运行用户代码时间+垃圾收集时间)
开启参数:-XX:+UseParallelGC,-XX:+UseParallelOldGC(互相激活)
cpu>8 N=5/8 cpu<8 N=实际个数
-Xmx10M -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
-Xmx10M -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC
CMS并发标记清除ConcMarkSweepGC:获取最短回收停顿时间为目标的收集器
并发收集低停顿
节约内存空间,会有内存碎片
开启:-XX:+UseConcMarkSweepGC开启后会将UseParallelGC打开
开启后会用
1、初始标记,标记下关联对象,需要暂停所有的工作线程
2、并发标记,和用户线程一起,标记全部对象
3、重新标记,CMS remark ,标记产生变动的对象,需要暂停
4、并发清除,和用户线程一起,直接清理对象,不需要暂停
优点:并发收集低停顿
缺点:
1、并发执行对cpu的压力
2、采用的标记清除算法会导致大量的碎片
参数配置
-Xmx10M -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC
Serial Old
面向服务端的垃圾收集
-Xmx10M -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
不会产生内存碎片,添加了预测机制,用户可以指定期望停顿时间
Cms垃圾收集器虽然减少了暂停应用的运行时间,但是它还是存在着内存碎片问题
特点
Region区域化垃圾收集器,化整为零,避免全内存扫描,只需按照区域进行扫描
将平常的板凳区养老区编为不连续的内存区域,避免了全内存区gc操作,将整个内存区域分成大小相同的子区域,JVM启动时自动设置这些子区域的大小,G1只要求对象的存储逻辑连续,可以按需在平常的板凳代和老年代切换
启动时可以通过参数 -XX:G1HeapRegionSize=n可指定分区大小(1-32mb),为2的幂,默认将整堆划分为2048个分区,能够支持最大内存,32*2048=65536MB=64GB内存
回收步骤:
针对伊甸园区:小区域收集+形成连续的内存块,避免内存碎片
4步过程:
初始标记:只标记GC Roots能直接关联到对象
并发标记:进行Gc Roots Tracing的过程
最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
筛选回收,根据时间来进行价值最大化的回收
三步:开始G1+设置最大内存+设置最大停顿时间毫秒
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100
G1跟Cms的优势:
G1没有内存碎片,可以精确的控制停顿时间