一.JVM内存区域
a.线程私有区域(生命周期与用户线程强相关)
java虚拟机栈+本地方法栈,程序计数器
b.线程共享区(生命周期与JVM进程强相关)
java堆(gc堆)、方法区、运行时常量池
a.线程私有区域:
1.程序计数器(线程私有)
程序计数器是一块比较小的内存空间,可看作是当前线程所执行的字节码行号指示器
:记录当前所执行的字节码行号
程序计数器是唯一一块不会发生OOM(OutofMemoryError)的区域
线程私有(在单核处理器上)
JVM的多线程是通过线程轮流切换,并通过分配处理器执行时间的方式来实现,在某个确定的时刻,只有一个线程运行在CPU上
线程私有空间:每个线程独有的区域互不影响,独立存储
-Xss:栈大小
2.java虚拟机栈
JAVA虚拟机栈描述的是JAVA方法的内存模型,生命周期与用户线程相同
java方法执行的同时会创建一个栈帧用于存储局部变量表,操作树栈,动态链接,方法出口等信息,每个方法从调用到执行完毕的过程,对应于此
方法对应栈帧在java虚拟机栈中入栈出栈的过程。
局部变量表(所需内存在编译期间分配,执行时不会改变局部变量表的大小):存放编译期间可知的各种基本数据类型(8大基本数据类型)、对象引用(4个字节)
虚拟机栈一共会产生一下两种异常:
a. StackOverFlowError(栈溢出):线程请求的栈深度 > 虚拟机栈所允许的栈深度 (-Xss设置栈容量)
b.OOM异常:虚拟机栈在动态扩展时无法申请到足够内存,抛出OOM异常
3.本地方法栈
本地方法栈与JAVA虚拟机栈完全相同,本地方法栈为Native方法服务,虚拟机栈服务的java方法,Hotspot虚拟机中,本地方法栈与虚拟机栈是同一块区域
b.线程公有区域:
4.java堆(GC堆)
java堆是JVM管理的最大内存区域,线程共享,在JVM启动时创建,在JVM结束时销毁,所有的java实例与数组都在堆上分配。
-Xmx:设置堆的最大值
-Xms:设置堆的最小值
-xmn:新生代大小
5.方法区(OOM)
存储被虚拟机加载的类信息,常量,静态变量等数据。jdk 1.8 以前被称为"永久代",jdk1.8以后被无空间取代
关键字在方法区中存储
6.运行时常量池
运行时常量池是方法区的一部分,存放字面量与符号引用。
字面量:字符串,fianl常量,基本数据类型的值 int i=10;
符号引用:类和结构的完全限定名,子段的名称与描述符,方法的名称与描述符
动态内存回收:内存的开辟和回收无须用户管理,自动回收
二、垃圾回收算法与内存分配策略
1.判断对象无用(对象已死)
a.引用计数法
给每个对象对象附加一个引用计数器,每当引用一次此对象时,计数器+1;
引用失效时,计数器-1,任何一个时刻,当计数器为0的对象就是不能使用的对象,即对象已死
优点:实现简单,判断效率高,大部分场景下是一个不错的算法
缺点:无法解决循环引用问题(两个对象互相引用) 所以java不用它
2.可达性分析算法---- java采用
通过一系列称为GC Roots 的对象作为起点开始向下搜索对象,直到找到此对象,搜索走过的路程称为引用链
当一个对象到达GC Roots没有引用链时(到GC Roots对象不可达),证明此对象是不可用的
在java中,GC Roots对象包含一下几种:
1.方法区中常量、静态属性引用的对象
2.局部变量表中引用的对象
3.本地方法栈中引用的对象
3.JDK1.2 后对引用的扩充 (强>软>弱>虚) ********************************************
a.强引用(strong Reference):指的是代码中普遍存在的,类似于Object obj=new Object() 这类的引用。只要强引用还在,垃圾回收器永远不会回收掉被引用的对象实例
b.软引用(soft Reference):描述一些由于但不必须对象,被软引用关联的实例,在系统即将发生内存溢出(OOM)前,
会将软引用对象列入垃圾回收范围,进行第二次垃圾回收,如果此次GC后还是没有足够内存,才会抛出OOM,JDK1.2后,
使用softReference来实现软引用
c.弱引用(weak Reference):描述一些不必须对象,强度弱于软引用,被弱引用关联的实例,只能生存到下一次垃圾回收之前,
GC开始时不管内存是否够用,都会对被弱引用关联的对象进行GC。 JDK1.2后,使用 weak Reference来实现弱引用
d.虚引用(phantom Reference):也被称为幽灵引用或幻影引用,一个对象是否存在虚引用,对其生存时间完全没有影响,
也无法通过虚引用取得对象实例,设置虚引用的唯一目的是:此对象被垃圾回收时会收到一个通知
4.OOM异常
内存泄漏:泄漏对象无法被GC
内存溢出:对象确实应该存活,此时应调整JVM参数适当加大堆内存或者检查对象的生命周期是否过长
对象可以被GC(内存不够,调整参数就可以解决问题 :溢出)
对象不能被GC(内存不够,调整参数不能解决问题 :泄漏)
StackOverFlowError 异常
对象被标记为无用对象后的生命周期:(protected void finalize() throws Throwable{})
一个对象的彻底死亡需要经历两次标记过程:
a.阶段1
如果一个如果对象到任意一个GCRoots对象不可达,它将被第一次标记并且进行一次筛选。
筛选的依据为:此对象是否要执行finalize()方法,当此方法没有覆写finalize()方法或者finalize()方法没有被调用过,此时对象真正死亡
b.阶段2
如果对象覆写 finalize() 方法且未被java调用,此对象会被放置在F-Queue队列中,稍后由JVM回调finalize()方法,如果对象在finalize()
中又和GC Roots相连,则成功拯救自己,并未真正死亡。
一个对象的finalize方法,只会被JVM调用一次
JDK1.9已经被@Deprecated
回收方法区(了解):回收废弃常量与无用类
判定一个类为无用类的要求:
a.该类的所有实例已经被回收(JAVA类中不存在任何该类的实例)
b.加载该类的类加载器已经被回收
c.该类对应的class对象没有在其他任何地方被引用,无法通过反射访问该类的所有属性和方法
***** 分代收集算法:
JVM采用分代收集算法,将JAVA堆分为新生代与老年代,新生代中,对象朝生夕灭,每次gc都有大量对象死亡,因此采用复制算法,老年代中,对象存活率较高,因此采用标记-整理算法
三、垃圾回收算法(*************)
1.标记-清除算法
a.工作流程:
首先标记出所有需要回收的对象,清除阶段一次性回收所有被标记的对象
b.缺点
空间问题:产生大量不连续的空间碎片
效率问题:标记与清除的效率都比较低
2.复制算法(新生代垃圾回收算法)
a.工作流程
将可用内存按容量大小分为大小相等的两块,每次只使用其中的一块,当发生GC时,
将此区域中的所有存活对象一次性复制到另一块区域然后清空此区域。
2.JVM对复制算法的改进(所有的商用JVM都采用复制算法回收新生代空间)
a.JVM新生代内存分配
JVM将内存(新生代内存)分为一块较大的Eden(伊甸区)和两块较小的survior区,每次只使用Eden区和一块survior区,两块较小的survior区分别是(一块 from区域,一块是 To区域),GC发生时将Eden区和survior区的存活对象一次性复制到另一块survior区上,然后清理Eden区和用过的survior区。(循环往复)
HotSpot默认 Eden:survior = 8 : 1; 因此新生代的可用内存为90%
b.工作流程
I.当Eden区快满的时候,会触发第一次Minor gc,然后将存活的对象一次性复制到from 区
当Eden区再次发生Minor gc 的时候,会扫描 Eden区和from区,将存活的对象一次性复制到To区,
然后清空Eden区和from区,如此循环往复
II.部分对象会在from区与To区来回复制,如此交换15次(MaxTeuring Threshold 参数,默认为15),会将此对象放入老年代。
3.标记-整理算法(老年代垃圾回收算法)
a.工作流程:
标记时标记出所有存活对象,然后将所有存活对象向一端移动,随后清理掉存活边界以外的空间
b. 复制算法在对象存活率较高的情况下会进行较多的复制操作,效率较低,因此老年代不使用复制算法。
######################################################################################################
面试题:
a.Minor Gc 称为新生代gc :只在新生代发生的垃圾回收,新生代使用复制算法进行垃圾回收,发生频率快,回收速度也比较快
b.Major Gc (Full Gc)称为老年代gc :只在老年代发生的垃圾回收,出现Full GC通常会伴随至少一次的Minor GC ,full gc 速度较慢(比Minor GC 慢10倍以上),发生频率较低
四、垃圾回收器(JDK1.8 垃圾回收算法)
新生代垃圾回收器:serial(串行) ParNew(并行) Parallel Scavenge(并行)
老年代垃圾回收器:CMS(并发),Serial old(串行), parallel old(并行)
全区域垃圾回收期:GI (并发)
并行:多条垃圾回收线程并行工作,用户线程仍然等待
并发:用户线程与垃圾回收线程同时执行
吞吐量:CPU运行用户代码时间/CPU总时间(用户代码时间+GC时间)
STW:垃圾回收线程工作时,所有工作线程暂停直到垃圾回收线程工作结束
五、内存回收与分配策略
1.对象优先在Eden区分配
大多数情况下,对象优先在Eden区分配,当Eden区内存快满时,虚拟机会进行一次Minor gc
-XX:+printGcDetails:(打印具体的垃圾回收参数)
-Xmn 10M (分配的新生代大小)
2.大对象直接进入老年代
-XX:PretenureSizeThreshold 大于此参数的对象直接在老年代分配
3.长期存活对象直接进入老年代:
-XX:MaxTenuringThreshold=1; 年龄为1,直接移入老年代
当对象从Eden区移动到survior时,年龄为1,每当对象在from - to 复制一次,age+1;
4.动态对象年龄判定(优先级较高)
如果在survior空间中相同年龄的所有对象大小的总和 >
survior 大于等于该年龄的对象,无须等到MaxTenuringThreshold规定的年龄