JVM运行数据区
1.程序计数器
当前线程所执行的字节码的行号指示器.
字节码解释器通过改变计数器的值来选取下一条需要执行的字节码指令.
<1>执行java方法,则记录正在执行的虚拟机字节码指令地址
<2>执行native方法,计数器值为空
2.java虚拟机栈
(虚拟机栈为虚拟机执行java方法/字节码服务)
生命周期与线程相同,描述的是java方法执行的内存模型.
方法在执行同时会创建栈帧存储局部变量表,操作数栈,动态链接,方法出口
局部变量表存放编译器可知的基本数据类型,对象引用和returnAddress类型..
3.本地方法栈
本地方法栈为虚拟机执行Native方法服务.
4.java堆
存放实例对象,垃圾收集器管理的主要区域.(分代收集算法)
(内存回收)Java堆细分为:新生代和老年代.(Eden,From Survivor,To Survivor区)
(内存分配)能划分出多个线程私有的分配缓冲区(TLAB)
5.方法区(Non-Heap)
存储已被加载的类信息,常量,静态变量,即使编译器编译后的代码.
运行时常量池
存放编译器生成的各种字面量和符号引用.
6.直接内存(并不是虚拟机部分)
NIO机制(基于通道与缓冲区的I/O方式) Native函数库直接分配堆外内存.
对象的创建:
首先去检查指令参数在常量池定位到类的符号引用
检查这个符号引用的加载,解析和初始化状态.
(未找到,则进行响应的雷家挨)
接着分配内存
<1>指针碰撞(内存规整)
思路: 已分配内存..|..分界点指示器(指针)..|..未分配内存
<2>空闲列表分配(内存不规整)
思路:分配空间给对象实例并更新列表记录.
Tip:并发处理
<1>对分配内存空间做同步处理.
<2>分配线程在不同的小块内存(TLAB)
对象的内存布局
<1>对象头
存储对象运行数据(Mark word)+类型指针
<2>实例数据
(各个类型的字段内容)
存储顺序根据虚拟机分配策略
Tip:相同宽度的字段总被分配到一起
<3>对齐填充(占位符)
对象的访问定位
(通过栈上reference数据操作堆上具体对象)
<1>使用句柄(在Java堆上划分句柄池,存储对象的句柄地址)
<2>直接指针访问
额外概念:
多线程通过线程切换并分配处理器执行时间的方式实现.在任意时间,处理器都只会执行一条线程.因此每条线程都有一个独立的程序技术其,之间互相不影响,独立存储.
JNI指的是本地Native方法
并行:多条垃圾收集线程秉性工作,但用户线程此时人处于等待状态
并发:用户线程与垃圾收集线程同时进行,用户程序继续运行,垃圾收集程序运行 在另一CPU上.
cglib技术????
JVM内存溢出
1.堆溢出
2.方法区溢出
JDK1.6 常量池分配在永久代
String.intern()
(1)JDK1.6
会把首次遇到的字符串实例复制到永久代.
返回的也是永久代字符串的引用.
(2)JDK1.7
不会复制实例,只在常量池记录首次出现的引用,返回的实例是堆中.
3.虚拟机栈和本地方法栈溢出
(1)线程请求栈深度大于虚拟机允许的最大深度 StackOverFlowMemory
(2)扩展栈无法申请到足够内存 OutOfMemoryError
4.直接内存溢出 ???
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
-XX:+PrintGCDetails
Xms/Xmx设置堆的最小/大值 Xmn给新生代大小 Xss设置栈的容量
-XX:SurvivorRatio=8 Eden区:Survive区=8
-XX:PermSize=10M -XX:MaxPermsize=10M 设置永久代
-XX:MaxDirectMemorySize 设置直接内存
GC算法
1.引用计数算法 互相引用则引用计数永不为0.
2.可达性分析算法
CGRoots对象作为起始点通过引用链()
对象:
(1)虚拟机栈(栈帧中的本地方法栈)中引用的对象
(2)方法区中类静态属性引用的对象.
(3)方法区中常量引用的对象
(4)本地方法栈中JNI引用的对象
3.引用强度
(1)强引用 存在则GC永远不会回收掉被引用的对象
(2)软引用 内存不足,则被纳入回收
(3)弱引用 生存到下一次GC发生前
(4)虚/幽灵引用 被GC回收时收到系统通知
Tip:finalize()方法
若未执行过且未被虚拟机调用过则可执行 否则没必要执行
只会执行一次 (finalize重新印涌上任何一个对象即可不被GC)
回收方法区:(永久代)
回收废弃常量和无用的类
1.废物常量 没有引用
2.无用类:(1)所有实例被回收 (2)ClassLoader回收 (3)Class在任何地方都没有被引用
GC收集算法
1.标记-清除算法
标记出所有需要回收的对象,标记完成后统一回收所有标记对象
2.复制算法
可用内存划分为大小相等的两块
当其中一块用完,将存活对象复制到另外一块上,再清理使用过的内存空间.
Eden:Survive1:Survive2=8:1:1 (Eden+Survive1中存活对象-->Survive2)
当Survivor2空间不够用时,需要依赖其他内存进行担保分配.
3.标记-整理算法
标记-清除后,将所有存活的对象向一段移动,然后清理掉端边界以外的内存.
新生代使用复制算法,老年代使用标记-清理或标记-整理算法进行回收
GC收集器
新生代收集器:
1.Serial收集器
单线程收集器,只是用1个CPU和一条单线程收集器
(适合运行在Client模式下)
2.ParNew收集器
(适合运行在Server模式下)
3.Parallel Scavenge收集器
目的:达到可控制的吞吐量 运行用户代码时间/CPU总消耗时间
-XX:MaxGCPauseMIlls(停顿时间) -XX:GCTimeRatio(吞吐量大小)
GC停顿时间缩短:以牺牲吞吐量和新生代空间.(垃圾回收更频繁,吞吐量下降)
自适应调节策略
老年代收集器:
1.Serial Old收集器
2.Parallel Old收集器
3.CMS收集器(并发收集,低停顿)
目的:最短回收停顿时间
<1>初始标记 标记一下CGRoots能关联到的对象
<2>并发标记 可达性分析寻找 (时间长)
<3>重新标记 修正并发标记期间因用户程序运作导致标记变动的标记记录
<4>并发清除
缺点:(1)CPU资源敏感
默认启动回收线程数为(CPU+3)/4,随CPU数量的增加而下降.
(2)无法处理浮动垃圾
清理时其他程序运行产生垃圾只能下一次GC
(3)大量空间碎片 (因为基于标记-清除算法实现)
G1收集器(老年代,新生代通用)
<1>并行和并发 (利用多CPU和多核)
<2>分带收集
<3>空间整合 整体(标记-整理)局部(复制)
<4>可预测的停顿 实时的可预测停顿时间模型
将整个java堆分为多个大小相等的区域(Region)
G1通过跟踪Region的垃圾堆积的价值大小
(回收所获得的空间大小以及回收所需时间的经验值)
在后台维护优先列表.优先回收价值最大的Region.
解决Region中对象被其他Region引用的问题.
Region设置对应的Rembered Set.
当引用对象的数据进行写操作时,产生writer Barrier暂时终端操作,检查饮用对象是否处于不同的Region之中.
通过CardTable将引用信息记录到Remembered Set中.
运作:
<1>初始标记 标记CGRoots直接关联的对象
<2>并发标记 进行可达性分析,找出存活的对象
<3>最终标记 修改并发标记期间标记变化的标记记录,将对象变化记录在Rembered Set Logs.
<4>筛选回收 对回收价值和成本进行排序.
内存分配和回收策略
新生代GC(Minor GC)
老年代GC(Full GC)
1.对象优先分配在新生代Eden区中分配.
Eden区没有空间时,将发起一次Minor GC.
新生代总可用空间为(Eden+From Survive)
TIp:当Eden+From Survive区无法容纳下一个对象时,将发生Minor GC.无法放入To Survice中时,则使用分配担保机制转移到老年代.
2.大对象(需要连续内存空间的java对象)直接进入老年代
(-XX:PretenureSizeThreshold)
3.长期存活的对象进入老年代
对象年龄计数器
对象在Eden出生后并经过第一次Minor GC仍然存活并被Survivor中熬过n(默认15)次则晋升到老年代 (-XX:MaxTenuringThreshold)
4.动态对象年龄判断
当Survivor空间相同年龄的总和大于空间的一半,年龄大于或等于则进入老年代.
(-XX:MaxTenuringThreshold)
5.空间配担保
检查老年代最大可用连续空间 冒险(Minor GC/Full gc不冒险)