期末作业制作网站,网站建设设计流程,苏州市建设安全监督局网站,在国内可以做国外的网站吗在《Java 虚拟机规范》的规定里#xff0c;除了程序计数器外#xff0c;虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError #xff08;下文称 OOM#xff09;异常的可能。
Java堆溢出
Java 堆用于储存对象实例#xff0c;我们只要不断地创建对象#xff0c;并…在《Java 虚拟机规范》的规定里除了程序计数器外虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError 下文称 OOM异常的可能。
Java堆溢出
Java 堆用于储存对象实例我们只要不断地创建对象并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象那么随着对象数量的增加总容量触及最大堆的容量限制后就会产生内存溢出异常。
下面代码限制Java堆的大小为20MB不可扩展(将堆的最小值-Xms参数与最大值-Xmx参数 设置为一样即可避免堆自动扩展)通过参数-XX:HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常的时候Dump出当前的内存堆转储快照以便进行事后分析。
/**
* Java堆内存溢出异常测试
* VM Args:-Xms20m -Xmx20m -XX:HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {static class OOMObject {}public static void main(String[] args) {ListOOMObject list new ArrayListOOMObject();while (true) {list.add(new OOMObject());}}
}运行结果
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3404.hprof ...
Heap dump file created [22045981 bytes in 0.663 secs]Java堆内存的OutOfMemoryError异常是实际应用中最常见的内存溢出异常情况。出现Java堆内存 溢出时异常堆栈信息“java.lang.OutOfMemoryError”会跟随进一步提示“Java heap space”。
要解决这个内存区域的异常常规的处理方法是首先通过内存映像分析工具(如Eclipse Memory Analyzer)对Dump出来的堆转储快照进行分析。第一步首先应确认内存中导致OOM的对象是否是必要的也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
如果是内存泄漏可进一步通过工具查看泄漏对象到 GC Roots 的引用链找到泄漏对象是通过怎样的引用路径、与哪些 GC Roots 相关联才导致垃圾收集器无法回收它们根据泄漏对象的类型信息以及它到 GC Roots 引用链的信息一般可以比较准确地定位到这些对象创建的位置进而找出产生内存泄漏的代码的具体位置。如果不是内存泄漏换句话说就是内存中的对象确实都是必须存活的那就应当检查Java虚拟机的堆参数(-Xmx与-Xms)设置与机器的内存对比看看是否还有向上调整的空间。再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况尽量减少程序运行期的内存消耗。
虚拟机栈和本地方法栈溢出
由于HotSpot虚拟机中并不区分虚拟机栈和本地方法栈因此对于HotSpot来说-Xoss参数(设置 本地方法栈大小)虽然存在但实际上是没有任何效果的栈容量只能由-Xss参数来设定。关于虚拟机栈和本地方法栈在《Java虚拟机规范》中描述了两种异常:
1如果线程请求的栈深度大于虚拟机所允许的最大深度 将抛出 StackOverflowError 异 常 。2如果虚拟机的栈内存允许动态扩展当扩展栈容量无法申请到足够的内存时将抛出OutOfMemoryError异常。
《Java虚拟机规范》明确允许Java虚拟机实现自行选择是否支持栈的动态扩展而HotSpot虚拟机的选择是不支持扩展所以除非在创建线程申请内存时就因无法获得足够内存而出现 OutOfMemoryError异常否则在线程运行时是不会因为扩展而导致内存溢出的只会因为栈容量无法容纳新的栈帧而导致StackOverflowError异常 。
方法区和运行时常量池溢出
由于运行时常量池是方法区的一部分所以这两个区域的溢出测试可以放到一起进行。前面曾经提到 HotSpot 从 JDK 7 开始逐步“去永久代” 的计划并在 JDK 8 中完全使用元空间来代替永久代的背景故事在此我们就以测试代码来观察一下使用“永久代”还是“元空间”来实现方法区对程序有什么实际的影响。
String::intern()是一个本地方法它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串则返回代表池中这个字符串的String对象的引用否则会将此String对象包含的字符串添加到常量池中并且返回此String对象的引用。
在 JDK 6 或更早之前的 HotSpot 虚拟机中常量池都是分配在永久代中我们可以通过-XX:PermSize和-XX:MaxPermSize限制永久代的大小即可间接限制其中常量池的容量首先以 JDK 6 来运行以下代码
/**
* VM Args:-XX:PermSize6M -XX:MaxPermSize6M
* 运行时常量池导致的内存溢出异常
*/
public class RuntimeConstantPoolOOM {public static void main(String[] args) {// 使用 Set 保持着常量池引用避免 Full GC 回收常量池行为SetString set new HashSetString();// 在short范围内足以让6MB的PermSize产生OOM了 short i 0;while (true) {set.add(String.valueOf(i).intern());}}
}运行结果
Exception in thread main java.lang.OutOfMemoryError: PermGen spaceat java.lang.String.intern(Native Method)at org.fenixsoft.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java: 18)从运行结果中可以看到运行时常量池溢出时在OutOfMemoryError异常后面跟随的提示信息 是PermGen space说明运行时常量池的确是属于方法区(即 JDK 6 的 HotSpot 虚拟机中的永久代)的 一部分。
而使用 JDK 7 或更高版本的 JDK 来运行这段程序并不会得到相同的结果无论是在 JDK 7 中继续使 用-XX:MaxPermSize参数或者在 JDK 8 及以上版本使用-XX:MaxMetaspaceSize参数把方法区容量同样限制在 6MB也都不会重现 JDK 6 中的溢出异常循环将一直进行下去。出现这种变化是因为自 JDK 7 起原本存放在永久代的字符串常量池被移至 Java 堆之中所以在 JDK 7 及以上版本限制方法区的容量对该测试用例来说是毫无意义的。这时候使用-Xmx参数限制最大堆到 6MB 就能够看到以下两种运行结果之一具体取决于哪里的对象分配时产生了溢出:
// OOM异常一:
Exception in thread main java.lang.OutOfMemoryError: Java heap spaceat java.base/java.lang.Integer.toString(Integer.java:440)at java.base/java.lang.String.valueOf(String.java:3058)at RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:12)// OOM异常二:
Exception in thread main java.lang.OutOfMemoryError: Java heap spaceat java.base/java.util.HashMap.resize(HashMap.java:699)at java.base/java.util.HashMap.putVal(HashMap.java:658)at java.base/java.util.HashMap.put(HashMap.java:607)at java.base/java.util.HashSet.add(HashSet.java:220)at RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java from InputFile-Object:14)本机直接内存溢出
直接内存 (Direct Memory) 和堆内内存相对应堆外内存就是把内存对象分配在 Java 虚拟机的堆以外的内存这些内存直接受操作系统管理而不是虚拟机这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。它的容量大小可通过-XX:MaxDirectMemorySize参数来指定如果不去指定则默认与Java堆最大值(由-Xmx指定)一致下面代码越过了DirectByteBuffer类直接通过反射获取Unsafe实例进行内存分配Unsafe类的getUnsafe()方法指定只有引导类加载器才会返回实例体现了设计者希望只有虚拟机标准类库里面的类才能使用Unsafe的功能在 JDK 10 时才将Unsafe的部分功能通过VarHandler 开放给外部使用因为虽然使用 DirectByteBuffer 分配内存也会抛出内存溢出异常但它抛出异常时并没有真正向操作系统申请分配内存而是通过计算得知内存无法分配就会在代码里手动抛出溢出异常真正申请分配内存的方法是Unsafe::allocateMemory ()。
/**
* 使用unsafe分配本机内存
* VM Args:-Xmx20M -XX:MaxDirectMemorySize10M
*/
public class DirectMemoryOOM {private static final int _1MB 1024 * 1024;public static void main(String[] args) throws Exception {Field unsafeField Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe (Unsafe) unsafeField.get(null);while (true) {unsafe.allocateMemory(_1MB);}}
}运行结果
Exception in thread main java.lang.OutOfMemoryErrorat sun.misc.Unsafe.allocateMemory(Native Method)at org.fenixsoft.oom.DMOOM.main(DMOOM.java:20)由直接内存导致的内存溢出一个明显的特征是在 Heap Dump 文件中不会看见有什么明显的异常情况如果发现内存溢出之后产生的 Dump 文件很小而程序中又直接或间接使用了 DirectMemory(典型的间接使用就是NIO)那就可以考虑重点检查一下直接内存方面的原因了。