方法区->类信息,静态变量
堆->数组对象
虚拟机栈-> 方法
本地方法栈->本地方法库 native
线程隔离 ,比较小的内存空间,当前线程所执行的字节码的行号
线程是一个独立的执行单元,由 CPU执行
唯一没有 OOM 的地方,由虚拟机维护,所以不会出现 OOM
执行的是Java方法
方法的调用就是栈帧入虚拟机栈的过程
栈帧:局部变量表(变量) 、操作数栈(存放a+b的结果 )、 动态链接(对对象引用的地址),方法出口(return的值)
线程请求的栈深度大于虚拟机所允许的深度StackOverflowError
执行的是 native 方法的一块 java内存区域,一样有栈帧
hotspot将 Java 虚拟机栈和本地方法栈合二为一
jvm标准是 java 虚拟机栈和本地方法栈分开
java内存中存放对象实例的区域,几乎所有的对象实例都在这里分配
所有线程共享
新生代、老年代
jmap -heap pid;
各个线程共享的内存区域
存储已被虚拟机加载的类的信息、常量、静态变量、即时编译器编译后的代码等数据
Hotspot用永久代实现方法区(让垃圾回收器可以管理方法区),对常量池的回收和卸载
方法区会抛出 OOM,当他无法满足内存分配需求时
运行时常量池是方法区的一部分,Class 中除了字段、方法、接口的 常量池,存放编译器生成的字面量和符号引用,这部分内容由类加载后进入方法区的运行时常量池中存放。
StringTable是HashSet结构
方法区的一部分,受到方法区的限制,依然会 OOM
<init> -> static方法 static代码块
1) 指针碰撞(内存比较整齐)
步骤:1. 分配内存 2. 移动指针,非原子步骤可能出现并发问题,Java虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性
2)空闲列表(内存比较乱)
存储堆内存空闲地址
步骤:1.分配内存 2. 修改空闲列表地址 非原子步骤可能出现并发问题,Java虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性
对象属性的值->实例数据
对象头 64 位机器存 64 位,32 位机器存 32 位,8 的倍数
对比:
新生代:对象在 Eden 区出生后,当 Eden 区满时,发生一次 Minor GC后,如果对象还存活且能被s1区域容纳,使用复制算法将存活对象复制到s1,然后一次性清除 (Eden + s0) (此时对象年龄 +1,年龄达到 15 后进入老年代)然后将 s0 ,s1互换位置,最终得到一个空的新生代(Eden + s0, s1一直处于非活动被动状态)
-Xmn 设置年轻代 -XX:NewRatio 设置年轻代与老年代的大小比例
-XX:OldSize 设置老年代大小
-XX:MaxPermSize 设置永久代(方法区)
-Xmx -Xms 设置堆空间大小
当对象实例分配给一个变量时,该变量计数设置为 1,当任何其他变量被赋值为这个对象的引用的时,计数+1 (a =b,则b的引用对象实例计数器+1),当一个对象实例的某个引用超过了生命周期(方法执行完)或者被设置为一个新值,则该对象的实例引用计数器 -1
无法解决循环引用
可达性分析
GC Root (虚拟机栈中的引用的对象、本地方法栈中引用的对象、方法区静态属性引用的对象、方法区常量引用的对象)
标记需要回收的对象,在标记完成后统一回收
不足:
1.效率问题,标记清除 2 个过程效率都不高
2.空间问题,标记清除后产生大量不连续的内存碎片,碎片过多当程序需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发一次垃圾回收动作
内存块 A存活的对象复制到内存块 B (Survivor to)里,然后将内存块A (Eden + Survivor from)清空,
只有少部分对象移动,更多的对象是要被回收的
Eden:Survivor from:Survivor to=8:1:1
98%对象“朝生夕亡”,新生代可用内存容量 90%(80%+10%),98%的对象可回收是一般情况,当小于 90%的对象被回收的时候(10%以上的对象存活时),则 Survivor to 空间不够,则需要依赖老年代进行分配担保
老年代不适合复制算法
步骤:
单线程垃圾回收器,用户线程到安全点先暂定,然后 GC 线程单线程串行进行,等 GC 线程回收完,然后用户线程再继续
特点:Stop the world
场景:桌面应用 (gc时间短)
用于新生代,client 端
Serial收集器的多线程版本
用于新生代,唯一能和CMS 收集器(老年代收集器)配合工作,运行在 server 模式下
-XX:ParallelGCThreads 限制垃圾收集器线程数 = CPU 核数(过多会导致上下文切换消耗)
并行:多条垃圾收集线程并行工作,用户线程仍然处于等待状态
并发:用户线程与垃圾收集器同时执行,用户线程和垃圾线程在不同 CPU 上执行
新生代收集器,复制算法,并行的多线程收集器
关注吞吐量优先的收集器(吞吐量 = CPU 运行用户代码执行时间/CPU 执行总时间 ,比如: 99%时间执行用户线程,1%时间回收垃圾,这时吞吐量为 99%)高吞吐量可以高效率利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不需要太多的交互任务
CMS 关注缩短垃圾回收停顿时间,适合与用户交互的程序,良好的响应速度能提升用户体验
-XX:MaxGCPauseMillis 参数 GC 停顿时间,参数过小会频繁 GC
-XX:GCTimeRatio 参数,默认 99%(用户线程时间占 CPU 总时间的 99%)
是Serial 收集器的老年代版本
单线程老年代收集器,采用“标记-整理”算法
是 Parallel Scavenge收集器的老年代版本
多线程老年代收集器,采用“标记-整理”算法
获取最短回收停顿时间为目标的收集器,采用“标记-清除”算法,用于互联网、B/S 系统重视响应的系统
触发阈值:老年代或永久代达到 92%
-XX:+CMSClassUnloadingEnabled 让 CMS 可以回收 Perm 区
步骤:
缺点 :
面向服务端应用的垃圾收集器
Region->Remembered Set (解决 循环引用 )
检查 Reference (程序对reference类型写操作,检查 reference 引用类型)
步骤:
优势:
Java 堆分布图
对象分配的规则:
大对象是指需要大量连续内存空间的 Java 对象,最典型的大对象是是那种很长的字符串以及数组
-XX:PretenureSizeThreshold 设置大于该值的对象直接分配在老年代,避免在 Eden 区以及 2 个Survivior区之间发生大量的内存复制
逃逸分析:分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,称为方法逃逸。甚至还有可能被外部线程访问到,比如赋值给类变量或其他线程中访问的实例变量,称为线程逃逸。
栈上分配:把方法中的变量和对象直接分配到栈上,方法执行完后自动销毁,不需要垃圾回收介入,从而提高系统性能
-XX:+DoEscapeAnalysis 开启逃逸分析(jdk1.8默认开启 )
-XX:-DoEscapeAnalysis 关闭逃逸分析
jmap -heap 9366;
jmap -histo 9366 | more; 显示堆中对象统计
jmap -dump:format=b,file=/Users/mousycoder/Desktop/a.bin 9366 生成dump文件
-Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/mousycoder/Desktop/
jhat /Users/mousycoder/Desktop/java_pid9783.hprof 图形分析Heap
select s.toString() from java.lang.String s where (s.value != null && s.value.length > 1000 )
shutdownHook 在关闭之前执行的任务
jstack -l -F pid 强制输出
基于 JMX 的可视化监视、管理工具
开启 JMX 端口
nohup java -Xms800m -Xmx800m -Djava.rmi.server.hostname=192.168.1.250 -Dcom.sun.management.jmx
remote.port=1111 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar hc-charging-
server.jar &
互联网开发流程
Jconsole 内存分析思考过程
Minor GC:当 Eden 区满,触发 Minor GC
FullGC:
建议:
解决方法:list.contain->set.contain->布隆过滤器(用户量大和用户量小系统解决方案不一样)
解决方法:jstack 以及 new thread带上名称
FullGC 出现正常频率为一天 1~2 次
解决方案:jmap , heap dump on oom + jhat
堆外内存( Unsafe的 allocateMemory ,ByteBuffer.allocateDirect,Runtime.getRuntime().freeMemory()可以查看不占用heap堆大小)
heap堆使用率很低,但是有 OOM 以及 Full GC
解决方法:btrace 跟踪DirectByteBuffer构造函数,非 Java层面使用 Google Perftools分析
解决方案: MQ
本文由博客一文多发平台 OpenWrite 发布!