JVM与垃圾回收

JVM内存管理

JVM是一种规范。JVM的跨平台性。JVM的语言无关性。

常见的JVM实现:Hotspot(Oracle公司)、Zing(C4垃圾回收算法)、毕昇(华为公司)

JVM运行时数据区域

运行时数据区域分为线程共享区和线程私有区

线程共享区包含:方法区、堆。运行时常量池在方法区中

线程私有区包含:虚拟机栈、本地方法栈、程序计数器

方法区(存储类加载Class类、静态常量(基本数据类型)、常量(基本数据类型)、static静态代码块、运行时常量池)

成员变量指向(对象)在类加载的时候不会执行,运行时成员变量会存储在堆中,

堆(存储对象实例)

栈(存储局部变量、引用对象)先进后出

运行时数据区外还存在一个直接内存,又称为堆外内存。

虚拟机栈 (先进后出)

虚拟机栈存放的方法的栈帧

虚拟机栈存储当前线程运行java方法所需的数据、指令、返回地址

-Xss 256K 限制大小

栈溢出一般是出现了死递归

栈帧

栈帧包含:局部变量表、操作数栈(不会被回收,会被复用)、动态连接、完成出口(返回地址)

程序计数器

程序计数器记录的是字节码运行的行号,记录的是偏移量

直接内存

Java中直接内存(堆外内存)有哪些:使用Unsafe操作本地内存、JNI或者JNA程序操作了本地内存

//直接分配128的直接内存
ByteBuffer buffer=ByteBuffer.allocateDiret(128*1024*1024);

直接内存的申请绕过了JVM的垃圾回收。好处是速度会稍微快点。缺点就是容易忘记回收造成内存泄漏。多线程申请内存会造成覆盖。

JVM内存处理全流程
  1. JVM申请内存
  2. 初始化运行数据区
  3. 类加载 会把类,静态变量和常量以及加了static的东西放入方法区
  4. 执行方法 运行方法创建栈,创建虚拟机栈。执行main方法的栈帧
  5. 创建对象

JVM编译:

  1. .java文件经过javac指令编译成.class文件

  2. 类加载把.class文件加载到方法区交由JVM执行引擎去执行

  3. jvm把字节码翻译成机器码

堆空间分代划分

分为老生代、新生代(Eden、From、To)。老生代存放的是经过多次GC垃圾回收没有回收掉的对象。

新生代经过15次GC后会晋升为老生代。对象经历一次垃圾回收,没有被回收掉。age+1。age达到15 晋级老生代。底层记录对象的年龄是用4位二进制,最大值位1111也就是15。

JHSDB工具查看内存。

JVM哪些区域会发生内存溢出:堆、虚拟机栈、方法区

对象的分配

JVM中对象的创建过程

类加载----》

检查加载(JVM遇到一条字节码new指令)-----》

分配内存(划分内存的方式:指针碰撞、空闲列表。解决并发安全:CAS加失败重试、本地线程分配缓冲)----》

内存空间初始化(“零”值)—》

设置(对象头)----》

对象初始化(构造方法)

使用指针碰撞内存必须是规整的。如果内存不规整,或者存在内存碎片就需要使用空闲列表方式。

CAS:是CPU的一个指令。多线程使用时使用CAS比较和交换。

本地线程分配缓冲:预先给每个线程划分一块内存区域(java8以后使用此种方式,禁止以后使用的就是CAS的方式)

对象分为:

1、对象头(Mark Word存储对象自身的运行时数据(哈希码、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳)、类型指针、若为对象数组,还应有记录数组长度的数据)

2、实例数据

3、对象填充(非必须)

对象的访问定位:使用句柄或者直接指针的方式。目前主流的JVM使用的是直接指针方式

垃圾回收

垃圾回收GC,需要判断对象的存货

判断对象的存活 :
  • 引用计数算法 没法解决循环引用的关系

  • 可达性分析(根可达)

  • 经过判断对象存活后还会有一个Finalize 缓冲期。Finalize是类的自我拯救。因为是另起了一个线程,所有优先级比较低 ,不推荐使用。

GC roots:

从根找可达 根有 静态变量----》线程栈变量-----》常量池----》JNI(指针)—》内部引用(class对象、异常对象Exception。类加载器)—》同步锁----》内部对象----》临时对象(跨代引用)

class对象要回收的条件。比较苛刻,要满足所有条件:
  1. class new 出的所有对象都要回收

  2. 对应的类加载器 也要回收

  3. 类 Java.lang.class对象,

  4. 任何地方没有引用,并且无法通过反射调用这个类的方法

  5. 参数控制

各种引用
  • 强引用 = 不会被回收
  • 软引用 SoftReference 发生垃圾回收,可能会回收
  • 弱引用 WeakReference 只要垃圾回收,就会被回收
  • 虚引用 PhantomReference 随时都会被垃圾回收回收
对象的分配策略

几乎所有的对象都在堆中进行分配,对象触发逃逸分析会把对象会分配到栈中(java 10000次)

对象的分配原则
  • 对象优先在Eden分配 (常用)

  • 空间分配担保

  • 大对象直接进入老年代

  • 长期存活的对象进入老年代 (常用)

  • 动态对象年龄判定

虚拟机的优化技术
  • 逃逸分析+触发JIT(热点数据) 对象会分配到栈中

  • 本地线程分配缓冲

垃圾回收算法
  • 复制算法 加强版(Appel式的复制回收算法)用于新生代垃圾回收

新生代内存分配规则:Eden:form:to=8:1:1

特点:实现简单、运行高效。没有内存碎片。空间利用率只有一半

  • 标记-清除算法 用于老生代

根据可达性分析标记引用的对象,然后清除没有引用的对象

特点:位置不连续,产生碎片。可以不暂停。

  • 标记-整理算法

根据可达性分析标记引用的对象,先标记,后整理内存空间。 最后整体清理

特点:没有内存碎片。指针需要移动。

垃圾回收器
  • 单线程垃圾回收器
  • 多线程并行垃圾回收器
  • 并发垃圾垃圾回收器
举例常见的垃圾回收器
  • Serial 回收新生代 复制算法 回收器类型属于单线程

  • Serical Old 回收老年代 标记整理算法 回收器类型属于单线程

  • Paraller Scavenge 回收新生代 复制算法 回收器类型属于多线程并行垃圾回收器

  • Paraller Old 回收老年代 标记整理算法 回收器类型属于多线程并行垃圾回收器

  • PaeNew 回收新生代 复制算法 回收器类型属于多线程并行垃圾回收器

  • CMS 回收老年代 标记清除算法 回收器类型属于并发的多线程回收器

  • G1 跨新生代和老年代 标记整理+化整为零算法 回收器类型属于并发的多线程回收器

CMS的标记清除算法的过程减少了暂停卡顿:初始标记 并发标记 重新标记 并发清除

CMS中的问题:CPU敏感 浮动垃圾 内存碎片

面试题

常量池(方法区):

  • Class常量池(静态常量池)

  • 运行时常量池

  • 字符串常量池

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值