Java 虚拟机 垃圾回收机制,堆内存分代存储,类的加载与卸载
==============================================
1:内存模型
==============================================
线程共享区域:堆(heap)和 方法区(Method Area)
线程私有:程序计数器,java虚拟机栈,本地方法栈。
堆(heap):new 出来的对象存放位置(存放过大,会导致内存溢出)
方法区:存放静态属性和常量那些(存放过大,会导致内存溢出)
程序计数器:可以理解为记录当前线程 执行到哪一行或哪一个指令。以便线程切换回来时,可以从当前指令继续执行。
java虚拟机盏:存放栈帧,可以理解每调用一个方法,就有一个栈帧放进虚拟机盏中,压栈太多会导致栈溢出(也就是方法调用的层级太深)。
本地方法栈:和虚拟机盏类似,但这个栈是用来存放 Native方法时的栈帧。
==============================================
2:内存模块划分
==============================================
【1:回收算法---- 分代收集算法】
【可达性分析算法】标记对象是否需要被回收的算法
对象引用都是以引用链为路径互相引用。如果一个对象的引用链没有连接上任何 根节点(GC Roots ),那就说明这个对象是没用的,是需要被回收的。所谓的根节点引用,就是:静态属性,常量属性,栈帧引用。 还有 Native方法引用的变量(千万不要说成员变量)的引用。如果没有这四个的引用,即代表这个对象是没用的。
把对象的存活周期分为:新生代和老年代
-------新生代 : 采用 复制算法。(针对对象存活率低使用的算法)
-------------------- 复制算法 :将内存一分为二,分为两个同样大小的区域。每次只用一个区域,等这个区域接近使用完了,会把这个区域的存活对象(可达性分析算法)全部复制到另一个区域,然后把这半个区域全部清空。每次都是对整个半区域进行回收。
缺点:每次只能用半个内存区域,内存空间使用率太低了
优点:效率高,每次只回收半个区域。不用考虑内存碎片的问题,因为复制过后会重新排列,剩余内
存空间都是完整连续排列的。
-------老年代 : 采用 标记-清除算法 或 标记-整理 算法 (针对对象存活率高使用的算法)
--------------------标记-清除 算法:通过可达性分析算法标记需要回收的对象,然后标记后的对象会被GC线程去回收。
缺点1> 效率问题,标记和效率的清楚都不高
缺点2> 空间问题,清除后会产生大量不连续的内存碎片。如果后续要给一个大的对象分配内存空间,这些内存碎片都不足以有足够的空间时,会再次出发GC进行回收。
--------------------标记-整理 算法:通过可达性分析算法标记需要回收的对象,然后标记后的对象会被GC线程去回收。是《标记清除算法》一个加强版。不直接对可回收对象进行清理,而是让存活的对象往一边移动,让所有的存活对象在内存空间里连续排序。然后直接清理掉边界外的无用对象。
优点1> 解决了内存碎片的问题
---------------------【标记-清除 算法】 示例图
---------------------【标记-整理 算法】 示例图
【2:垃圾回收器--回收机制】
垃圾回收器扫到了需要被回收的对象,且这个对象的finalize() 从来没被调用过,会调用对象的 finalize()。如果在finalize()对象重新和根节点连接上(比如:把对象赋值给一个静态变量),那这个时候不会回收这个对象。但如果,对象的finalize() 方法已经被调用过,就不会再调用finalize(),直接就回收了这个对象。
【3:内存分配和回收策略】
【1:分区】
新生对象内存分配分为:Eden > Survivor > 老年区 优先级依次递减
新生区:Eden 和 Survivor (复制回收算法)
老年区:老年区 (标记-整理回收算法)
注意:并不是所有对象都会先在新生区存储的,比如一个对象特别大,就会直接进入 老年区 。
所以,避免大对象的创建,会提前触发垃圾回收器去回收垃圾,以提供足够连续的空间去安置大对象。
(java虚拟机可以通过参数设定 大对象的 界限)。超过直接放进老年区。
【2:对象在内存区域的移动】
<规则1> 年龄计数器,每被垃圾回收器扫过一次,年龄就+1。Eden被扫过一次,存活下来就会移到Survivor区域,Survivor区域被扫到15次还存活就会移动老年区。当然,这些界限的参数是可以控制的。
<规则2> 年龄的判断规则不是唯一的。如果Survivor区域所有同龄对象所占内存之和 大于 Survivor空间的一半,那么大于这个年龄的对象都会被移到老年区域。
<规则3>
1> 对象刚开始只会存在于Eden区和Survivor 的 From区,Survivor的“To”是空的。
2> 紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到年龄阈值的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。
3> Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
==============================================
2:类加载
==============================================
类的加载分为五大步:
【1:加载】
1:获取此类的二进制流(分别可以从:zip,网络(applet),运行时才生成(动态代理))
2:将这个流转成方法去运行时的数据结构
3:生成这个类的Class对象,作为此类在方法区的数据访问接口
【2:连接】
细分:验证,准备,解析
验证: 验证编码是否符合UTF-8,常量类型是否支持等等这些
准备: 分配内存,初始化变量
解析: 虚拟机将常量池符号引用替换为直接引用
【3:初始化】
类加载最后一步
【4:使用】
使用就是使用对象的属性
【5:卸载】
卸载,可以理解为引用进程被杀死