JVM的 OutOfMemoryError异常
Java堆溢出A. 关于 “这里面讲保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象” 理解GC Roots是不是有4类 a它可以是方法局部变量表中引用b方法区类静态成员引用常量引用如 static final String。 c. 本地方法栈中 JNI 引用d. 同步锁持有的对象。 jvm内部引用如基本类型 Class 对象如 int.class、常驻异常如 NullPointerException 类对象等只要上面4种持有引用就表示GC Roots到对象之间有可达路径B. 当设置-XX:HeapDumpOnOutOfMemoryError时出现内存溢出jvm会dump内存快照到启动 java -jar … 命令时所在目录是吗答 是的。 格式长这样 java_pid.hprof 通常只对 Java Heap Space 和 Metaspace 溢出触发直接内存溢出Direct buffer memory等不会触发需额外配置如下表示堆出现异常Java heap spacejava.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid3404.hprof …Heap dump file created [22045981 bytes in 0.663 secs]C, 内存溢出分两种一种是内存泄漏Memory LeakGC Roots的引用链一直存在即对象一直无法被jvm回收。 第二种就内存耗尽Memory Exhaustion 即程序中对象必须存在内存不足了。关于内存泄漏处理策略是通过Eclipse Memory Analyzer这个工具中的 Dominator Tree 表示谁控制了最多内存” 的树状结构Shallow Heap该对象自身占用的内存如对象头 字段值不含引用对象Retained Heap单位byte该对象及其所有可达对象且不被其他路径引用的总内存 → 这才是真正“它独占”的内存Percentage占整个堆的百分比按 Retained Heap 计算重点看 Retained Heap 和 Percentage —— 它告诉你哪个对象“吃掉”了最多内存是泄漏嫌疑最大的目标下一步如何深挖这个 main Thread– 步骤 1右键 java.lang.Thread … main Thread → “Show in Dominator Tree”点击左侧小三角 , 就可以看出是哪个点击Percentage最大的一个左侧标签栏有一个Attributes可以看具体是什么对象– 步骤 2右键该线程 → “Merge Shortest Paths to GC Roots”这是关键操作它会显示从 GC Root 到该线程的最短引用路径即谁让这个线程“活”着它又持有哪些对象– 步骤 3右键该线程 → “List Objects” → “with outgoing references”查看它直接引用了哪些对象比如是不是有个 HashMap 或 ArrayList 占了大部分 Retained Heap/**VM Args-Xms20m -Xmx20m -XX:HeapDumpOnOutOfMemoryErrorauthor zzm*/public class HeapOOM {static class OOMObject {}public static void main(String[] args) {List list new ArrayList();while (true) { list.add(new OOMObject()); }}}虚拟机栈和本地方法栈溢出的理解栈溢出jvm不区分是虚拟机栈和还是本地方法栈。jvm栈的大小通过-Xss参数 来设置出现如下两种情况会就会抛出StackOverflowError异常a. 如果超过栈的深度。如在不断递归达到指定深度时默认Linux 64位通常为 1MB, 可以达到1000~2000层递归/**VM Args-Xss128k*/public class JavaVMStackSOF_1 {private int stackLength 1;public void stackLeak() {stackLength;stackLeak();}public static void main(String[] args) {JavaVMStackSOF_1 oom new JavaVMStackSOF_1();try {oom.stackLeak();} catch (Throwable e) {System.out.println(“stack length:” oom.stackLength);throw e;}}}b. 在方法中定义异常大量“基本类型”不是引用对象的变量对象的大小在堆中从而增大了本地变量表的长度。-3 -Xss参数 默认在liunx下面不能小于228k. 不然jvm启动会有一个提示The Java thread stack size specified is too small. Specify at least 228k-4. 线程创建过多会出现OutOfMemoryError如果线程栈的很大这种情况更容易发生这与栈空间是否足够没有关系。这种情况在我公司的项目就遇到过– 这种oom的异常现象从JDK 7起 提示信息中“unable to create native thread”后面 虚拟机会特别注明原因可能是“possiblyout of memory or process/resource limits reached”– 它的原因是如果单个进程是 2G 减去 堆方法区(-XX:MaxMetaspaceSize)直接内存(-XX:MaxDirectMemorySize)》 剩下给虚拟机栈和本地方法栈分配。如果线程量大而且每个线程-Xss又很大就会出现“unable to create native thread”公式最大线程数 ≈ (物理内存 - 堆(-Xmx) - Metaspace(-XX:MaxMetaspaceSize) - 直接内存(-XX:MaxDirectMemorySize)) / (-Xss)– 解决方法如果无法减少线程数时无法加大进程的内存时可以减小堆的大小及线程栈-Xss的大小。方法区和运行时常量池溢出的理解在jdk8中方法区都在元空间中存放的内容如类名、 访问修饰符、 常量池、 字段描述、 方法描述等。 所以jdk8之后就没有java.lang.OutOfMemoryError: PermGen space而是java.lang.OutOfMemoryError: Metaspace1.1 触发场景A. 动态生成大量类如 Spring CGLib 代理、Groovy 脚本、OSGi 模块 B. 类加载器泄漏ClassLoader 未卸载 → 其加载的类无法回收字符串常量池已经放到了堆中了以前jdk8以前是放在方法区。– String::intern()的行行A. 会直接在堆中字符串常量池找是否存在如果存在直接返回引用。B.如果不存在直接从堆中找到该string对象放到常量池中intern()不会创建对象并返回引用。public static void main(String[] args) {String str1 new StringBuilder(“计算机”).append(“软件”).toString();System.out.println(str1.intern() str1); // 这个在jdk8时是true 因为intern()在常量池中找计算机软件,发生是第一次所以从堆中找该对象StringBuilder(“计算机软件”)放到常量池中。所以它们是一样的。String str2 new StringBuilder(“ja”).append(“va”).toString();System.out.println(str2.intern() str2); // 这个在jdk8时是false因为因为“java”这个字符串在如 rt.jar 中的类名就已经出现过一次 字符串常量池中已经有了和堆中新生的new StringBuilder(“java”)不一样。}-3. XX MetaspaceSize 指定元空间的初始空间大小默认约 20.8MB平台相关当元空间使用量 MetaspaceSize 时下次 Full GC 会尝试卸载无用类型卸载。 -XX MinMetaspaceFreeRatio 作用是在垃圾收集之后控制最小的元空间剩余容量的百分比 可减少因为元空间不足导致的垃圾收集的频率。疑问在项目中需要创建超极大量的对象从jstat -gcutil 时Metaspace一直在90%以上。答 这和创建超大量的对象与Metaspace 高占用无关。 和工程用到spring CGLIB、 AOP、ORM 等功能有关会在运行时动态生成大量新的类jstat -class输出示例:Loaded Bytes Unloaded Bytes Time25432 52345.6 0 0.0 12.34如果 ‘Loaded’ 数量巨大几万甚至更多并且 ‘Unloaded’ 为0或很少说明类在持续加载但没有被卸载。本机直接内存溢出的理解直接内存可以通过-XX:MaxDirectMemorySize参数来指定 如果不去指定 则默认与Java堆最大值由-Xmx指定 一致,但最高不超过4G.异常信息java.lang.OutOfMemoryError: Direct buffer memory。通过jstat -gcutil可以发现堆的使用率很低。AI的langchain,langgraph , java,spring,hadoop, flink流式计算, 心理学哲学相关知识探讨.本人邮箱luyllyl163.com