运行时内存
Posted wheleetcode
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了运行时内存相关的知识,希望对你有一定的参考价值。
1 程序计数器 线程私有,记录线程所执行的字节码行号指示器。
2 虚拟机栈 描述Java方法执行内存模型 , 进入一个方法创建栈帧。
3 Java堆 存放对象实例
4 方法区:编译后的代码数据,包括加载 的类信息,常量,静态变量。
5 常量池 :class文件常量池方法区一部分存放编译器生成的字面量和符号引用,加载时放入虚拟机的运行时常量池中
6 对像创建 : 1 类是否已经加载 2 为对象分配内存 类加载后,对象所需内存确定 , 内存空间初始化0,3 设置对象头部信息 (对象所属类,哈希等) 4 根据程序员意愿,执行init方法,为对象设置初值。
7 对象布局: 对象头(1 对象运行时数据 2 类型指针 数组还有数组长度), 实例数据信息 , 对其填充
8 对象访问定位 1 句柄 (对象数据指针 对象类型指针) 好处,reference存稳定句柄值,对象变化只改变句柄中对象数据指针, 2 直接 对象地址,好处 访问块。
9分析是内存溢出还是内存泄漏,如果内存泄漏,找出泄漏对象到GC roots的引用链,看为什么对象没有回收, 如果是内存溢出,看虚拟机堆是不是太小。看某些对象生命周期是否过长,尝试减少运行期间内存消耗。
10 哪些内存需要回收。Java堆,方法区
11 什么时候需要回收 对象死
1 引用计数算法:每一个地方引用对象,计数器加1 , 很难解决对象之间相互循环引用问题。
2 可达性分析 “GC Root” 对象为起点,从起点向下搜,所走过路径为引用链,当对象到“GC Root” 没有引用链时,对象不可达。
12 生存还是死亡 不可达对象, 检测对象是否有必要执行finalize方法,如果没有覆盖finalize方法或方法已经被虚拟机调用过,会直接回收,如果有必要执行方法,在这个方法中对象有可能存活。
public class JDBCTest{ public static JDBCTest j = null; public void isAlive() { System.out.println(" i am alive"); } @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); System.out.println("finalize exet"); j = this; } public static void main(String[] args) throws Exception { j = new JDBCTest(); j = null; System.gc(); Thread.sleep(500); if (j != null) { j.isAlive(); } else { System.out.println("j dead"); } j = new JDBCTest(); j = null; System.gc(); Thread.sleep(500); if (j != null) { j.isAlive(); } else { System.out.println("j dead"); } } }
13 回收方法区 1 废弃常量,无用类 1 类实例被回收 2 类classloader被回收 3 Class对象没有被引用
14 垃圾回收算法
1 标记 -清除 缺点 标记,清除效率不高 产生大量不连续碎片
2复制 分为大的eden和小的survivor 只用Eden ,垃圾回收时将存活对象放入survivor ,并清除Eden
3 标记 -- 整理 对垃圾回收后大部分对象都会存活使用 和标记-清除不同的是,让存活对象像一边移动,清除边界外内存。
4 分代收集 根据对象存活周期不同,新生代 ,每次垃圾收集都有大量对象死,使用复制算法 老年代 对象存活率高,标记清理或标记整理
15 hotSpot算法实现
1 枚举根节点(全局性引用,常量或静态属性 与执行上下文 栈帧本地变量表)stop the world 枚举跟节点时,必须停止所有线程,否则枚举不准确 ,编译类后使用oopMap数据结构记录哪个地方有引用,加快枚举跟节点速度
2 安全点 在安全点停下来GC, 是否有让程序长时间执行的特征 方法调用 ,循环跳转 异常跳转
如和在GC发生时让线程在安全点停下,1 抢占式 中断所有线程,如果发现有线程没有到安全点,线程跑到安全点 2 主动中断 在安全点轮询,如果需要GC,把自己中断。
3 如果线程处于sleep或阻塞,线程无法相应jvm中断请求,走不到安全点,需要安全区域:在这段代码片段中,引用关系不发生变化,任意地方开始GC安全。在线程要离开安全取时,检测是否完成枚举跟节点。
serial 新生代 垃圾收集时必须停止所有线程工作。并且只有一条线程垃圾回收 client模式默认
ParNew serial 多线程版本 , 可以有多条线程垃圾回收 server模式默认
parallel scavenge 和ParNew 很相似 , 多条线程垃圾回收 , 和上一个关注停顿时间不同,主要关注吞吐量(用户代码运行时间/总运行时间),适合和用户交互少的后台代码执行。可以直接设置吞吐量和最大停顿时间。
老年代
serial old serial老年代版本,单线程收集, 标记整理, client模式
Paraller old 是parallel scavenge老年代版本 多线程收集 在注重吞吐量和CPU资源敏感场合,优先Paraller old ,parallel scavenge组合
cms (concurrent mark sweep) 以获取最短回收停顿为目标,标记清除算法,
4个步骤 初始,重新 stop the world , 初始标记只标记GC Root直接关联对象,速度很快 并发标记 经行GC Roots Tracing过程,重写标记修正并发标记期间的修改,
缺点1 cms对CPU资源敏感 2 无法处理浮动垃圾 可能出现concurrent mode failure 失败导致另一次full gc,由于cms并发清理阶段用户线程还在运行着,伴随程序运行还有垃圾产生,垃圾出现标记过程后,cms无法在当次处理他们,只好下次gc,这部分垃圾为浮动垃圾 3 空间碎片
G1
并行 :多条垃圾回收线程一起执行
并发 :垃圾回收,用户程序一起执行
内存分配与回收策略
1 对象优先在Eden分配。当Eden区没有足够空间,虚拟机发起一次minor Gc
2 大对象直接进入老年代
3 长期存活对象进入老年代
4 动态对象年龄判定 survivor空间相同年龄所有对象大小和大于survivor空间一半,年龄大于等于该年龄对象进入老年代。
5 空间分配担保 在Minor GC前,会检测老年代最大可用连续空间是否大于新生代所有对象总空间,如果条件成立,Minor GC安全,如果不成立,检测是否允许担保失败,如果允许,,继续检测老年代最大可用空间是否大于历次晋升到老年代对象的平均大小,如果大于,经行minor GC, 如果小于,或者不允许担保失败,则经行FUll GC
命令行工具
jps jvm process status toll 显示指定系统内所有hotStop虚拟机进程
jstat jvm statistics monitotring toll 统计信息监视工具 本地或远程 类装载 内存,垃圾收集 jit编译等运行时数据
jinfo configuration info for java 查看和调整配置信息工具
jmap memory map for java 堆转储快照 (dump 文件) 查看堆,永久代详细信息。
jhat jvm heap analysis toll 分析jmap生成的堆转储快照,
栈帧用于支持方法调用和方法执行的数据结构,存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息,在编译代码时,栈帧需要多大局部变量表,多深操作数栈已经完全确定,并写入方法表code属性中,因此一个栈帧要分配多少内存,不受运行期变量数据影响。
jstack stack trace for java 当前线程快照,每条线程正在执行的方法堆栈集合,找出线程长时间停顿原因。
局部变量表:是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,编译为class文件时,指定了最大的大小。
操作数栈:先进后出栈,编译时确定最大深度
动态连接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,引用为了支持方法调用过程中的动态连接,字节码方法调用指令就以常量池中指向方法的符号引用作为参数,一部分在类加载阶段或第一次使用转换直接引用静态 一部分运行期转换直接引用。
方法退出:把当前栈帧出栈,恢复上层方法的局部变量表,和操作数栈,把返回值压人调用者的操作数栈中,调整pc计数器指向方法调用指令后面的一条指令。
符号引用变为直接引用 1 类加载阶段:解析调用 ,前提是方法在程序真正运行前就有一个可调用的版本,并且方法调用的版本在运行期间不改变,符编译期可知,运行期不变,静态方法和私有方法,final方法,构造器。非虚方法。
2 分派调用:
变量的类型分为静态类型和动态类型,编译器在重载时是通过参数的静态类型而不是实际 类型作为判定依据的,静态类型在编译器可知,在编译阶段javac会根据静态类型决定使用哪个重载版本,把方法的符号引用写到字节码指令中。
所有依赖静态类型定位方法执行版本的分派为静态分派,典型重载,发生在编译阶段,编译器确定方法重载版本。
虚拟机动态分配实现:在类方法区建立一个虚方法表,指向方法入口。
invokevirtual 1 找到操作数栈顶第一个元素所指向的对象的实际类型。在类方法区虚方法表找签名相同方法,返回方法直接引用。
以上是关于运行时内存的主要内容,如果未能解决你的问题,请参考以下文章
每当我运行我的片段时,这行代码 mapFragment.setRetainInstance(true);正在崩溃我的应用程序? [关闭]