Java 虚拟机在执行 Java 程序的过程当中会把他所管理的内存划分为若干个不一样的数据区域。Java 虚拟机规范将 JVM 所管理的内存分为如下几个运行时数据区:程序计数器、Java 虚拟机栈、本地方法栈、Java 堆、方法区。html
一,内存区域划分java
1.线程共享区域:算法
(1)Java堆(对象实例),GC的主要区域,会出现OutOfMemoryError数组
(2)方法区(加载的类信息,常量,静态变量,即时编译器编译后的代码)会出现OutOfMemoryError服务器
2.线程私有区域:数据结构
(1)虚拟机栈(操做数栈,动态连接,方法返回地址,局部变量) 多线程
用于支持虚拟机进行方法方法调用和方法执行的数据结构。生命周期与线程相同,每一个方法执行时会建立一个栈帧并入栈,对于执行引擎,活动线程中,只有栈顶的栈帧是有效的,称为当前栈帧,所关联的方法称为当前方法。并发
若是申请的栈深度大于虚拟机容许的栈深度则抛出StackOutflowErrorapp
若是在动态扩展时,没法申请到足够的内存则抛出OutOfMemoryError工具
单线程:不管是栈空间太大,仍是虚拟机内存过小,抛出的都是StackOutflowError
多线程:抛出OutOfMemoryError
局部变量表:是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。最小单位为变量槽(Slot)大小为32字节,对于超过32字节的变量,使用连续的槽进行存储
操做数栈:又被称为操做栈,最大深度在编译时肯定,32位数据类型占用容量为1,64为数据类型占用容量为2。在方法执行过程当中,根据字节码指令,进行入栈出栈操做。
动态链接:栈帧中存在一个指向运行时常量池的引用,对常量池中符号的引用若是在类加载阶段转换为直接引用则称为静态解析。在每一次运行期间转换为直接引用,称为动态链接。
方法返回地址:方法正常退出时,调用者的计数器指则为返回地址。方法异常退出时,返回地址是经过异常处理器来肯定的。
(2)本地方法栈(和虚拟机栈概念几乎相同,不一样的是服务于本地操做系统(Native)方法)
(3)程序计数器:一块内存较小的内存空间,是当前字节码执行的行号指示器,根据该指示器来肯定下一条须要执行的指令。
<注>Object obj = new Object();这段代码的执行会涉及到Java栈,Java堆,方法区三个重要的内存区域。假设该语句出如今方法体中,obj会做为引用类型存储到Java虚拟机栈的局部变量表中,实例对象则保存在java堆中。而该对象的地址信息(如对象类型,父类,实现的接口等)则保存在方法区中。
二.垃圾回收(GC)
垃圾回收(GC)指回收掉没用的内存数据,将空间释放出来以便存储新的数据。垃圾回收能够有效的防止内存泄漏。
1.GC类型
根据不一样区域发生的GC,可主要将GC分为YGC和FullGC
YGC:当新生代满了以后,会触发minor collection(YGC)。
FullGC:当整个堆内存达到阈值时,会触发major collection(Full GC),致使整个heap的回收。(应尽可能避免出现FullGC,由于该GC收集时间较长,频繁的FullGC会致使应用性能受到严重影响)
2.Java堆内存
新生代
Eden区:新生区,新建立的对象都存储在该区域
Survior区:幸存区,主要存储既未到达进入老年代的条件又位于To Survior的对象 Survior区由两部分组成
From区:新生代发生minorGC时,该区域内存会根据年龄决定去To区仍是老年代
To区:新生代发生minorGC时,该区域内存数据不会被清除
老年代:年龄达到老年的对象数据会从From Survior区进入该区域
永久代(java8以后叫作元空间,不属于堆):主要存储的是JVM运行时须要的类和方法,元空间的类的对象是在进行FullGC时才进行垃圾收集
3.内存申请过程
JVM会试图为java对象在Eden中初始化一块内存区域
当Eden空间足够时,内存申请结束,不然进入下一步
JVM试图释放在Eden中全部的不活跃对象(YGC),释放后若仍不足以放入新对象,则试图将Eden中部分活跃对象放入Survivor的To区,
若JVM的From Survivor区内存不足,则JVM会判断对象的年龄,而后选择性的将对象移动到To Survivor区或老年代。
若是老年代的内存不足,JVM会在老年代进行major collection(FULL GC)
完成垃圾回收后,若是Survivor和老年代仍然没法存放eden区中的数据,致使JVM没法在Eden区为新建对象分配内存区域,则出现OutOfMemeryError
4.内存的衰老过程
新建对象的内存都分配自eden区,MinorCollection(YGC)的过程就是将eden中的对象移动到空闲的To Survivor区中,将From survivor区中经历过必定次数YGC(次数能够i经过参数配置)的对象移动到老年代
5.经常使用的垃圾回收方法
是否进行垃圾回收,须要知道一个对象是否可用。
(1)引用计数算法:每当又一个地方引用一个对象时,计数器就加1,当引用失效时,计数器减一,任什么时候刻计数器为0的对象就是再也不被使用的对象(不能解决循环引用的问题)
(2)可达性分析算法:用于判断对象是否存活,基本思想是经过GC Roots对象做为根节点,从这些节点向下搜索,搜索的路径称为引用链,当一个对象到GC Roots没有任何引用链时就认为对象属于不可达,证实此对象时不可用的。
可做为GC Roots的对象:
虚拟机栈中引用的对象
方法区中类静态属性引用的对象或常量引用的对象。
本地方法栈中JNI(Native方法)引用对象
(3)不管是经过引用计数法,仍是可达性分析算法,都是为了判断对象是否存在引用。而引用又分为强引用和弱引用,软引用,虚引用
强引用:代码中广泛存在的,相似Object obj = new Object(),垃圾回收器永远不会回收这部分对象
软引用:用来描述一些有用可是非必需的对象,在内存即将发生内存溢出时,会回收这部分对象
弱引用:也是用来描述非必需的对象,可是比软引用更弱一些,被弱引用关联的对象只能生存道下一次垃圾回收发生以前
虚引用:也叫幽灵引用或幻影引用,是最弱的一种引用关系,不会影响到垃圾回收
(4)垃圾算法
标记-清除法:最基础的收集算法,分为标记和清除两个阶段,首先标出全部须要回收的对象,在标记完成以后统一回收标记对象。这种算法会产生大量不连续的内存碎片,堆内存使用率低
标记-整理法:首先标出全部须要回收的对象,而后将这部分对象清除,再将全部存活的对象向一端移动,这种算法不会出现内存碎片
分代收集算法:将内存划分为多块,而后再根据不一样区域的特色选择使用不一样的垃圾回收算法例如新生代每次垃圾回收后只会有少许对象存活,则使用复制算法,而老年代的存活率高,则可使用标记清理或者标记整理法
6.经常使用垃圾收集器
(1)串行收集器(Seiral Collector):最简单的垃圾收集器,基本上都是涉及单核环境下工做,几乎不会使用该收集器,由于它在收集时会暂停整个应用的运行。
使用方法:-XX:+UseSerialGC
(2)并行/吞吐优先收集器(Parallel/Throughput Collector):这是JVM默认的收集器,跟它的名字显示的同样,它最大的优势就是使用多个线程来扫描和压缩堆,缺点是在minorGC和fullGC时会暂停应用的运行,并行收集器适合能够容忍程序停滞的环境使用,它占用较低的CPU于是能够提升应用的吞吐
使用方法:-XX:UseParallelGC
(3)CMS收集器(CMS Collector):CMS使用的是并发的标记与清除,这个算法使用多个线程并发的扫描堆,标记不使用的对象,而后清除它们回收内存。
使用方法:-XX:UseConcMarkSweepGC,此时可同时使用-XX:UseParNewGC将并行收集做用于年轻代,新的JVM自动打开这个配置
原理:并发标记清除算法
流程:初始化标记->并发标记->并发预清理->从新标记->并发清理->并发重置
优势:减小了回收的停顿时间,回收效率高
缺点:产生的空间碎片多,须要更多的CPU资源,须要更大的堆空间,下降了堆空间的利用率
使用场景:程序对停顿比较敏感,而且应用程序运行时能够提供更大的内存和更多的CPU
(4)G1收集器(Garbage First Collector):G1收集器适用于堆内存大于4G的JVM,它会将堆分红多个区域,大小从1MB-32MB,并使用多个后台线程来扫描这些区域,优先会扫描最多垃圾的区域
使用方法:-XX:+UseG1GC
Java8和G1收集器:G1收集器在Java8上最好的优化是String去重,String对象和它内部使用的char[]数组会占用比较多的内存,由于优化过的G1收集器会把重复的String对象指向同一个char[]数组,避免多个副本存在堆里
使用方法:-XX:UseStringDeduplication
原理:标记整理算法
流程:初始标记->并发标记->最终标记->筛选回收
优势:并行于并发,分带收集,空间整合,可预测停顿
缺点:须要更大的内存和更多的CPU
使用场景:对堆内存的使用率要求较高,对停顿时间容忍性较低,而且堆内存足够大
(5)G1和CMS的区别
CMS是以获取最短回收停顿时间为目标的收集器,基于标记-清除算法实现,比较占用CPU资源,容易产生内存碎片
G1是面向服务器端的垃圾收集区,是JDK9的默认收集器,基于标记-整理算法实现,可利用多核,多CPU,保留分带,实现可预测可控停顿
7.一次完整的GC
当新生代发生minorGC时,Eden区中存活的对象都被复制到To Survivor区,而后From Survivor区中的对象根据年龄决定区老年代仍是To survivor区,而后清空Eden区和From Survivor区,最后再交换From区和To区。
当老年代中若是对象到达了回收的年龄,则会根据对象的垃圾回收器使用对应的算法进行内存Full GC。
永久代(元空间)在进行FullGC时进行垃圾回收
三.JVM内存模型
1.内存屏障:为了保障顺序和可见性的一条CPU指令
2.重排序:为了提升性能,编译器和处理器会对执行语句进行重排序
3.happen-before:操做间执行的顺序关系,有些操做优先于有些操做发生
4.主内存:共享变量存储的区域
5.工做内存:每一个线程copy的本地内存,存储了该线程已读/已写的共享变量的副本
四.JVM经常使用参数
-server -Xms512m -Xmx512m -Xss1024k
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20
-XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly
server模式启动
-Xms512m:最小堆内存512M
-Xmx512m:最大堆内存512M
-Xss1024k:每一个线程栈空间1M
-XX:PermSize=256M:永久代256M
-XX:MaxPermSize=512m:永久代最大512M
-XX:MaxTenuringThreshold=20:最大转为老年代检查次数20
XX:CMSInitiatingOccupancyFraction:CMS回收开启时间,内存占用80%
五.JVM经常使用工具
http://www.javashuo.com/article/p-nrqxnuwc-bm.html