市场调研数据网站,手机网页开发工具,谷歌搜索入口,html5写的网站有什么好处引言
本文总结自周志明的《深入理解Java虚拟机》第二章部分内容。
这部分内容#xff0c;可以为后续性能调优方面的工作起到铺垫作用。
一、什么是 OutOfMemoryError
OurOfMemory 简称“OOM”#xff0c; 直译为“内存耗尽”或“内存溢出”#xff0c;当然#xff0c;并…引言
本文总结自周志明的《深入理解Java虚拟机》第二章部分内容。
这部分内容可以为后续性能调优方面的工作起到铺垫作用。
一、什么是 OutOfMemoryError
OurOfMemory 简称“OOM” 直译为“内存耗尽”或“内存溢出”当然并不是真的内存耗尽了它指的是 JVM 的几个逻辑分区的内存不够用了无法为新的对象分配空间。
在 JVM 的几个主要内存分区中JVM 栈、本地方法栈、计数器、方法区、Java 堆只有计数器不会出现这种严重错误也就是说我们常说的堆和栈等都有可能出现 OOM 的问题。
二、Java 堆溢出
由于 Java 中最常见的内存分配就是对象因此经常要分配内存用于创建对象的堆区是出现OOM问题的最常见内存分区。
对于 Java 堆的内存溢出原因其实非常简单。因为堆是用于存储对象的因此只要不断地创建对象并且保证 GC Roots 到对象之间有可达路径避免垃圾回收机制清除这些对象那么堆就必然会出现 OOM 问题。
如果要试验堆上的 OOM 最快的方法就是将堆的分配大小调的低一些并且不可扩展。
跟在 java 启动指令之后的两个最基本的堆大小分配参数是-Xms 最小堆内存 和 -Xmx 最大堆内存。将这两个参数的值设置为相同即可避免堆内存的自动扩展。
案例演示
案例使用 JDK 1.8 IDE是 Eclipse内存分析工具Eclipse Memory Analyzer简称 MAThttps://www.eclipse.org/mat/downloads.php 使用如上图所示的虚拟机参数执行程序即可发生堆内存的 OOM 错误。-XX:HeapDumpOnOutOfMemoryError 参数可以让虚拟机在出现内存溢出时Dump转储倾倒出当前的内存堆转储快照以便事后进行分析。 dump文件的名称类似 java_pid31228.hprof需要使用 MAT 打开并分析。具体分析过程暂时不做详细介绍 总之当出现堆溢出时一般的手段是先通过内存映像分析工具如Eclipse Memory Analyzer对 Dump 出来的堆转储快照进行分析重点是确认内存中的对象是否是必要的也就是要先分清楚到底是出现了内存泄漏Memory Leak还是内存溢出Memory Overflow。
内存泄漏表示对象已经无用但是GC并没有回收需要进一步通过工具查看泄漏对象到 GC Roots 的引用链掌握了泄漏对象的类型信息和 GC Roots 引用链 信息就可以比较准确地定位出泄漏代码的位置。而内存溢出
内存溢出表示对象却是还存活着导致GC 无法回收“有用的”对象因此就需要检查 堆参数-Xms 、-Xmx与机器物理内存对比看是否还可以调大或者从代码上检查是否有对象生命周期过长、持有状态时间过长的情况尝试减少程序运行期的内存消耗。
三、虚拟机栈溢出 在 HotSpot 虚拟机中是不区分 JVM 栈和本地方法栈的。设置本地方法栈大小的参数 -Xoss 实际上并无效果。 栈容量只由 -Xss 参数设定。 Java 虚拟机规范规定了两种栈异常 1、如果线程请求的栈深度大于虚拟机所允许的最大深度抛出 StackOverflowError 异常。 2、如果虚拟机在扩展栈时无法申请到足够的内存空间抛出 OutOfMemoryError 异常。 实际上StackOverflowError 针对的是单独的虚拟机栈而 OutOfMemoryError 则描述的是所有虚拟机栈。因为一个应用程序中很可能存在多个线程因此这样区分异常可能是为了更精确的描述出现的问题。
相对来说虚拟机栈内存出现问题多数都是 StackOverflowError而且Overflow的话会有错误堆栈可以阅读相对比较好找到问题所在。而且如果使用虚拟机默认参数栈深度在大多数情况下达到1000 到 2000 个栈帧完全没有问题。对于正常的方法调用和递归这个深度应该完全够用。
因此如果虚拟机栈发生 OOM 很可能是由于栈深度过大换句话说-Xss 参数值过大。
案例演示
以下代码可能在Windows上运行会使系统假死建议将重要文件和工作保存。 由于我的电脑本身执行上面的程序就会出现操作系统假死因此这里贴出书中的执行结果 Exception in thread main java.lang.OutOfMemoryError: unable to create new native thread
四、方法区和运行时常量池溢出
运行时常量池是方法区的一部分因此可以通过常量池溢出来测试这两个区域的溢出情况。 上面的代码通过 CGLib 动态生成大量的 Class 加载如 方法区。需要通过maven引入CGLib依赖 !-- https://mvnrepository.com/artifact/cglib/cglib --dependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.2.5/version/dependency
但是代码执行后并未出现方法区内存溢出的问题。书中贴出的方法区内存溢出异常报错如下
Caused by:java.lang.OutOfMemoryError : PermGen spaceat ...at ...at ...