JVM 内存区域深入
用到的一些VM参数:
-Xms30m 堆初始内存
-Xmx100m 堆最大内存
-XX:MaxMetaspaceSize=30m 最大元空间内存
-XX:+UseConcMarkSweepGC 开启
-XX:-UseCompressdOops 关闭对象指针压缩
JHSDB: 可视化的映射JVM运行信息的工具
复制sawindbg.dll 到jdk/bin下
目录到lib下,启动: java -cp ./sa-jdi.jar sun.jvm.hotspot.HSDB
栈的优化技术
数据共享:前后两个栈帧共享一部分变量内存区域, 上一个栈帧的操作数栈,被作为下一个栈帧的局部变量表
内存溢出
1.堆溢出 对象回收有问题
2.栈溢出 死递归
3.方法区异常 class,静态变量等太多
4.本机直接内存溢出 错误使用了unsafe, bytebuffer等
如果GC占用98%的资源同时回收不足2%, 抛出OOM:GC overhead limit execeeded
常量池(方法区的一部分)
1.Class常量池 类信息, 字面量, 字符引用转换直接引用
2.运行时常量池 运行时对象的直接引用 -->jdk1.7后在堆中, 但是仍然属于方法区
3.字符串常量池 解决string创建效率的问题
字符串不可变 1) 安全 2) hash唯一 3)
JVM遇到new关键字
1.检查类是否被加载/解析/初始化过
2.分配内存
方式:1)指针碰撞 2)空闲列表(使用CMS垃圾回收器, 只能用此方式)
并发安全性: 1)CAS 2)本地线程分配缓冲 TLAB(在Eden中划分1%的空间)
3.内存空间初始化
本地基础类型变量"零值"初始化
4.对象头设置
5.对象初始化
构造方法执行
对象的内存布局
1.对象头
2.实例数据
3.对象填充(对象头+实例数据必须满足8字节倍数的空间)
对象的内存布局, (8字节的整数), 对象占用的内存空间必须为8字节倍数的整数, 不足的部分会被填充, 这样处理,利于垃圾回收,内存分配
对象的访问定位
1.句柄(Refrence-->句柄-->对象), 稳定性好
2.直接指针(Refrence-->对象), 效率高, 稳定性低, 因为对象被移动或回收后, 需要修改正在运行中的线程中Refrence的数据, hotspot 使用此方式
对象的存活
1.无任何引用指向的对象, 该对象变成了垃圾
2.循环引用(引用计数法解决不了),需要使用可达性分析算法(根可达 GCRoots)
作为根的对象主要是以下4种:
-- 静态变量
-- 线程栈变量
-- 常量池
-- JNI(指针)
另外还有:
-- JVM内部对象,class对象,系统类加载器,Exception, Error等
-- 所有被同步锁持有的对象
-- JVM内部JMXbean, JVMTI 中注册的回调
-- 临时性对象,跨代引用的对象
3.对象覆盖了finalize方法,且该方法内又把对象建立了引用, 则不会被回收....被救回来了
但对象的finalize方法只会执行一次,也就是说只能被救回一次...
Class回收的条件
1.该类所有实例被回收
2.该Class的类加载器ClassLoader被回收
3.该类Class对象没有在任何地方被引用, 也无法在任何地方被反射访问
4.JVM参数控制
-Xnoclasgc (禁用class垃圾回收)
各种引用
1.强引用 -->一般代码中的对象引用方式
2.软引用 SoftReference 垃圾回收时,如果要发生OOM, 则会先回收此类引用对象 ,多用于缓存场景
3.弱引用 WeakReference 垃圾回收时一定会回收此类引用对象
4.虚引用 PhantomReference 应用很少, 一般用于检测GC是否正常, 对象何时被回收
对象的分配策略
1.绝大多数对象被分配在堆中
2.一部分被分配在栈中,满足的条件:
经过逃逸分析,对象不大且只在当前方法使用,不符合逃逸条件, 则会被分配到栈空间中
-XX:-DoEscapeAnalysis 关闭逃逸分析,所有对象都建立到堆上了
3.-XX:PretenureSizeThreshold=4m 设置大对象的条件
4.-XX:MaxTenuringThreshold=15 设置对象到老年代的GC年龄