当前位置: 首页 > news >正文

国企网站建设要求百度推广怎么做免费

国企网站建设要求,百度推广怎么做免费,大连金州房价,怎样才能制做免费网站文章目录概述运行时数据区域程序计数器Java虚拟机栈本地方法栈Java堆方法区运行时常量池直接内存HotSpot虚拟机对象探秘对象的创建第一步第二步第三步第四步最后一脚对象的内存布局对象头Header第一部分第二部分实例数据Instance对齐填充Padding对象的访问定位句柄直接指针对象… 文章目录概述运行时数据区域程序计数器Java虚拟机栈本地方法栈Java堆方法区运行时常量池直接内存HotSpot虚拟机对象探秘对象的创建第一步第二步第三步第四步最后一脚对象的内存布局对象头Header第一部分第二部分实例数据Instance对齐填充Padding对象的访问定位句柄直接指针对象的访问定位小结实战OutOfMerroyErrorJava堆溢出解决之道虚拟机栈和本地方法栈溢出解决之道方法区和运行时常量池溢出运行时常量池溢出方法区溢出方法区和运行时常量池溢出小结本机直接内存溢出概述 内存泄露 Memory Leak 内存溢出 Out Of Memory 有JVM存在无需为每个new操作写配对的delete/free代码不易出现 Out Of Memory 或 Memory Leak。但是愿望是美好的该出现的问题还是会出现了解JVM如何使用内存有助于解决问题。 A memory leak in Java is when objects you aren’t using cannot be garbage collected because you still have a reference to them somewhere. An OutOfMemoryError is thrown when there is no memory left to allocate new objects. This is usually caused by a memory leak, but can also happen if you’re just trying to hold too much data in memory at once. ——What is the difference between an OutOfMemoryError and a memory leak 运行时数据区域 程序计数器 Program Counter Register PCR 是一块较小的内存空间可看作当前线程所执行的字节码的行号指示器。 在VM概念模型中字节码解释器工作时就是通过改变PCR的值来选取下一条需要执行的字节码指令。 以下功能需要这个计数器来完成。 分支循环跳转异常处理线程恢复… 由于JVM多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的在任何一个确定的时刻一处理器对于多核处理器来说是一个内核都只会执行一条线程中的指令。一时刻一核一指令 因此为了线程切换后能恢复到正确的执行位置每条线程需要一个独立PCR各条线程PCR互不影响独立存储这内存区域被称为“线程私有”。线程独立性 若线程正执行一个Java方法这PCR记录正在执行VM字节码指令的地址若正执行是Native方法这PCR则为空Undefinded。 这内存区域是唯一一个在JVM规范没有规定任何OutOfMemoryError OOME情况的区域 Java虚拟机栈 VM Stack线程私有声明周期与线程相同。 VM Stack描述的Java方法执行的内存模型每个方法在执行的同时会创建一个栈帧Stack Frame。 Stack Frame用于 存储局部变量表操作数栈动态链接方法出口信息 每一个方法从调用直至执行完成的过程就对应着一个Stack Frame 在VM Stack中入栈到出栈的过程。 粗糙说法Java内存区分为 Heap 堆内存Stack 栈内存 这里指 VMStack Java内存区域划分实际上远比这复杂。 局部变量表存放 编译期可知的各种基本数据类型boolean/byte/char/short/int/float/double对象引用 reference类型 它不等同于对象本身可能是 一个指向对象起始地址的引用指针一个代表对象的句柄或其他与此对象相关的位置 returnAddress类型指向了一条字节码指令的地址 64位的double/long类型数据会占用2个局部变量空间Slot其余基本类型占1个。 局部变量表所需的内存空间在编译期间完成分配当进入一个方法时这方法需要在帧中分配多大的局部变量空间是完全确定的在方法运行期间不会改变局部变量表的大小。 JVM规范中这区域规定2种异常状况 StackOverflowError SOE当线程请求栈深度大于VM所允许深度时。OutOfMemoryError OOMEVM Stack可动态扩展当前大部分JVM都可动态扩展只不过JVM规范中允许固定程度的VM Stack当扩展时无法申请到足够的内存。 本地方法栈 Native Method Stack NVS 与 VM Stack作用相似它们区别为 VM Stack为VM执行Java方法也就是字节码服务。NVS则为VM使用到Native方法服务。 VM规范未强制规定 对NVS中方法使用的语言使用方式与数据结构并没有强制规定。 不同VM各有发挥。 Sum HotSpot VM 直接把 VM Stack 和 NVS 合二为一。 会抛SOE和OOME异常。 Java堆 Java Heap 线程共享 通常Java堆是JVM所管理内存最大一块。 VM启动时创建。 此内存区域的唯一目的就是存放对象实例几乎所有对象实例在这里分配内存。 JVM规范所有对象实例以及数组都要在堆上分配。 但随着JIT编译器技术等发展所有对象在堆上分配不是那么绝对。 Java堆是GC管理主要区域因此也被称为GC堆Garbage Collected Heap 从内存回收的角度看由于现在收集器基于分代收集算法所以Java堆中可细分为新生代 和 老生代更细致的 Eden空间、From Survivor 空间、To Survivor空间等。 从内存分配的角度来看线程共享的Java堆可划分多个线程私有的分配缓冲区Thread Local Allocation BufferTLAB 无论怎样划分目的始终是 更好回收内存更快分配内存。 通过-Xmx/-Xms控制扩展大小 若堆中没有内存完成实例分配并且堆也无法再扩展时将会抛出OOME 方法区 Method Area 线程共享。 它用于存储已被VM加载的 类型信息常量静态变量即时编译器编译后的代码等数据。。。 JVM规范 把 方法区描述为堆的一个逻辑部分但它别名叫做Non-Heap非堆目的与Java堆区分开。 大部分HotSpot VM开发者更愿意成方法区称为永久代Permanent Generation 二者并不等价只因HotSpot设计团队选择把GC分代收集扩展至方法区或说使用永久代来实现方法区而已这样HotSpot的GC可以像管理Java堆一样管理这部分内存能够省去专门为方法区编写内存管理代码工作。 其他虚拟机BEA JRockit/IBM J9等不存在永久代的概念。 原则上如何实现方法区属于VM实现细节不受虚拟机规范结束但使用永久代来实现方法区现在看不是好主意因为易于 内存溢出 问题永久代有-XX:MaxPermSize的上限 JDK1.7的HotSpot已把原在永久代的字符串常量池移出. GC比较少在方法区内出现但并非数据进入了方法区就如永久代的名字一样“永久”存在了这区域的内存回收目标主要是针对常量池的回收 和 对类型的卸载。 当内存不满足抛OOME 运行时常量池 Runtime Constant Pool RCP是方法区的一部分 Class文件中除了有类的版本、字段、方法、接口等描述信息外还有一项信息是常量池Constant Pool Table用于存放编译期生成的各种字面量和符号引用这部分内容将在类加载后进入方法区的RCP中存放。 JVM对Class文件每一部分的格式都有严格规定每一字节用于存储那种数据都必须符合规范上的要求才会被VM认可、装载和执行。 但对于RCPJVM规范没有做任何细节的要求不同的提供商实现的虚拟机可以按照自己的需要实现区域。 通常除了保存Class文件中描述的符号引用外还会翻译出来的字节引用也存储在RCP中。 运行时常量池 相对于Class文件常量池 的另外一个重要特征是具备动态性。 Java语言并不要常量一定只有编译器才能产生也就是并非预置如Class文件中常量池的内容才能进入方法区RCP运行期间也可能将新的常量放入池中如String类的intern()方法。 当内存不满足抛OOME 直接内存 Direct Memory 并不是虚拟机运行时数据区的一部分也就是JVM规范中定义的内存区域。 这部分内存也频繁使用而也可能导致OOME. 在JDK1.4中新加入了NIO类引入一种基于通道Channel与缓冲区Buffer的IO方式它可以使用Native函数库直接分配堆外内存然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能因为避免了在Java堆和Native堆中来回复制数据。 显然Direct Memory分配不受Java 堆大小的限制但是既然是内存肯定还是会受到本机总内存包括RAM一级SWAP区或者分页文件大小 以及 处理器寻址空间的限制。 服务器管理员在配置VM参数会根据实际内存设置-Xms等参数信息但经常忽略直接内存使得各种内存区域总和大于物理内存限制从而导致动态扩展时出现OOME. HotSpot虚拟机对象探秘 对于细节问题必须把讨论范围限定在具体的虚拟机和集中在某一个内存区域上才有意义。 对象的创建 JVM创建一个普通对象非数组或Class对象过程大概可分4步。 第一步 VM遇到一条new指令时首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用并且检查这个符号引用是否已被加载、解析和初始化过。 若没有那必须先执行相应的类加载过程。 第二步 在类加载检查通过后接下来VM将为新生对象分配内存。 对象所需内存的大小在类加载完成后完全确定为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。 两种分配方式: 指针碰撞 Bump the Pointer空闲列表Free List 假设Java堆中内存是绝对的完整的所有用过的内存放在一边空闲的内存放在另一边中间放着一个指针作为分界点的指示器那所分配内存就仅是那指针向空闲那边挪动一段与对象大小相等的距离这种分配方式称为“指针碰撞 Bump the Pointer” 若Java堆中的内存并不是规整的已使用的内存和空闲的内存相互交错那就无法简单地进行指针碰撞VM就必须维护一个列表记录上哪些内存块是可用的在分配的时候从列表中找到一块足够大的空间划分给对象实例并更新列表上的激励这种分配方式称为“空闲列表Free List”。 采用GC是否带有压缩整理功能 --决定–Java堆是否规整 --决定– ? 指针碰撞 : 空闲列表 因此使用Serial、ParNew等带Compact过程的收集器时系统采用的分配算法是指针碰撞 使用CMS这种基于Mark-Sweep算法的手机器时通常采用空闲列表 除了如何划分可用空间之外还有另外一个需要考虑的问题是对象创建在虚拟机中是否非常频繁的行为即使是仅仅修改一个指针所指向的位置在并发情况下也不是线程安全可能出现正在给对象A分配内存指针还没来得及修改对象B又同时使用了原来的指针来分配内存的情况。分配内存时的线程安全问题 解决上述问题有两种方案好似synchronized和ThreadLocal 对分配内存空间的动作进行同步处理——实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性把内存分配的动作按照线程划分在不同的空间之中进行即每个线程在Java堆中预先分配一小块内存称为本地线程分配缓存Thread Local Allocation Buffer, TLAB。哪个线程分配内存就在哪个线程上的TLAB上分配只有TLAB用完并分配新的TLAB是才需要同步锁定。VM是否使用TLAB可以通过-XX:/-UseTLAB参数来设定。 第三步 内存分配完成后VM需要将分配到的内存空间都初始化为零值不包括对象头如果使用TLAB这一工作也可以提前至TLAB分配时进行。这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用程序能访问到这些字段的数据类型所对应的零值。 第四步 VM对对象进行必要的设置例如 这个对象是哪个类的实例如何才能找到类的元数据信息对象的哈希码对象的GC分代年龄。。。 这些信息存放在对象的对象头Object Header之中。根据VM当前的运行状态的不同如是否启用偏向锁等对象头会有不同的设置方式。 最后一脚 在上面工作都完成之后从VM的视角来看一个新对象已经诞生但从Java程序的视角来看对象创建才刚刚开始前面那个顶多是个半成品——init方法还没有执行所有字段都还为零。所以一般来说由字节码是否跟随invokespecial指令所决定执行new指令之后会接着执行init方法把对象按照程序员的意愿进行初始化这样一个真正可用的对象才算完全产生出来。 对象的内存布局 在HotSpot VM中对象在内存中存储的布局可以分为3块区域对象头Header、实例数据Instance和对齐填充Padding 对象头Header 对象头包含两部分信息 第一部分 第一部分用于存储对象自身的运行时数据如 哈希码HashCodeGC分代年龄锁状态标志线程持有的锁偏向线程ID偏向时间戳。。。 这部分数据的长度在32/64位的VM未开启压缩指针中分别为32/64bit官方称它为“Mark Word” 第二部分 另一部分是类型指针即对象指向它的类元数据的指针VM通过这个指针来确定这个对象是哪个类的实例。 并不是所有VM实现都必须在对象数据上保留类型指针换句话说查找对象的元数据信息并不一定要经过对象本身。 若对象是一个Java数组那在对象头还必须有一块用于记录数组长度的数据因为VM可通过普通Java对象的元数据信息确定Java对象的大小但是从数组的元数组中却无法确定数组的大小。 实例数据Instance 对象真正存储的有效信息也是程序代码中所定义的各种类型的字段内容。 无论是从父类继承下来的还是在子类中定义的都需要记录起来。这部分的存储顺序会受到虚拟机分配策略参数FieldsAllocationStyle和字段在Java源码中定义顺序的影响。 对齐填充Padding 鸡肋仅仅起着占位符的作用。HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍。也就是对象大小必须是8字节的整数倍。当对象实例数据部分没有对齐时就需要通过对齐填充来补充。 对象的访问定位 建立对象是为了使用对象Java程序需要通过栈上的reference数据来操作堆上的具体对象。 由于reference类型在JVM规范中只规定了一个指向对象的引用并没有定义这个引用通过何种方式去定位、访问堆中的对象的具体位置所以对象访问方式也是取决于虚拟机实现而定的。 目前主流访问方式有使用句柄和直接指针两种 句柄 Java堆中将会划分出一块内存来作为句柄池reference中存储的就是对象的句柄地址而句柄中包含了对象实例数据 与 对象类型数据各自的具体地址信息。 直接指针 Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息而reference中存储的直接就是对象地址 对象的访问定位小结 这两种对象访问方式各有优势使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址在对象被移动垃圾收集时移动对象是非常普遍的行为时只会改变句柄中的实例数据指针而reference本身不需要修改。 使用直接指针访问方式的最大好处就是速度更快它节省了一次指针定位的时间开销由于对象的访问在Java中非常频繁因此这类开销积少成多也是一项非常可观的执行成本。 HotSpot使用直接指针进行对象访问的但从整个软件开发的范围来看各种语言和框架使用句柄来访问的情况也十分常见。 实战OutOfMerroyError 通过代码验证JVM规范中描述的各个运行时区域存储内容。希望读者在工作中遇到实际的内存溢出异常知道什么样的代码可能会导致这区域内存溢出以及出现这异常该如何处理。 例如VM启动参数 -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:PrintGCDetails -XX:SurvivorRatio8verbose /vɝ’bos/ adj. 冗长的啰嗦的 Java堆溢出 Java堆用于存储对象实例只要不断创建对象并且保证GC Roots到对象之间有达途径来避免垃圾回收机制清除这些对象那么再对象数量到达最大堆的容量限制后就会产生内存溢出异常 将堆的最小值-Xms和最大值-Xmx参数设置一致可避免堆自动扩展 -XX:HeapDumpOnOutOfMemoryError让VM在出现内存溢出时Dump出当前的内存堆转储快照以便事后进行分析 package com.lun.c02;import java.util.ArrayList; import java.util.List;/*** VM Args-Xms20m -Xmx20m -XX:HeapDumpOnOutOfMemoryError* author zzm*/ public class HeapOOM {static class OOMObject {}public static void main(String[] args) {ListOOMObject list new ArrayListOOMObject();while (true) {list.add(new OOMObject());}}}/*result:[GC (Allocation Failure) [PSYoungGen: 7793K-1001K(9216K)] 7793K-5334K(19456K), 0.1073292 secs] [Times: user0.09 sys0.00, real0.11 secs] [GC (Allocation Failure) [PSYoungGen: 9193K-1024K(9216K)] 13526K-11194K(19456K), 0.0330039 secs] [Times: user0.03 sys0.00, real0.03 secs] [Full GC (Ergonomics) [PSYoungGen: 1024K-1023K(9216K)] [ParOldGen: 10170K-10140K(10240K)] 11194K-11163K(19456K), [Metaspace: 2790K-2790K(1056768K)], 0.4100364 secs] [Times: user0.44 sys0.00, real0.41 secs] [Full GC (Ergonomics) [PSYoungGen: 8551K-8424K(9216K)] [ParOldGen: 10140K-8028K(10240K)] 18692K-16452K(19456K), [Metaspace: 2790K-2790K(1056768K)], 0.3584301 secs] [Times: user0.39 sys0.00, real0.36 secs] [Full GC (Allocation Failure) [PSYoungGen: 8424K-8423K(9216K)] [ParOldGen: 8028K-8016K(10240K)] 16452K-16440K(19456K), [Metaspace: 2790K-2790K(1056768K)], 0.2883919 secs] [Times: user0.30 sys0.02, real0.29 secs] java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid5784.hprof ... Heap dump file created [28135555 bytes in 0.213 secs] Exception in thread main java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:3210)at java.util.Arrays.copyOf(Arrays.java:3181)at java.util.ArrayList.grow(ArrayList.java:265)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)at java.util.ArrayList.add(ArrayList.java:462)at com.lun.c02.HeapOOM.main(HeapOOM.java:19) HeapPSYoungGen total 9216K, used 8703K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)eden space 8192K, 100% used [0x00000000ff600000,0x00000000ffe00000,0x00000000ffe00000)from space 1024K, 49% used [0x00000000fff00000,0x00000000fff7fd40,0x0000000100000000)to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)ParOldGen total 10240K, used 8016K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)object space 10240K, 78% used [0x00000000fec00000,0x00000000ff3d4398,0x00000000ff600000)Metaspace used 2822K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 302K, capacity 386K, committed 512K, reserved 1048576K*/ Java堆内存的java.lang.OutOfMemoryError是实际应用中常见的内存溢出异常情况。 解决之道 先通过内存影响分析工具如Eclipse Memory Analyzer对Dump出来的堆转储快照进行分析重点是确认内存中的对象是否是必要的也就是要先分清楚到底是出现了内存泄露Memory Leak还是内存溢出Memory Overflow从字面上理解吧 Eclipse Memory Analyzer使用方法 使用Help-Eclipse Marketplace安装Open Perspective中选择Memory AnalysisOpen Heap Dump 打开上例程序运行所产生的堆转储快照文件java_pid5784.hprof在classpath目录下 注意java_pid5784.hprof文件被打开后系统产生一堆文件 若内存泄露可一步通过工具查看泄露对象到GC Roots的引用链。于是就能找到泄露对象是同过怎样的路径与GC Roots相关联并导致GC无法自动回收它们的。掌握了泄露对象的类型信息及GC Roots引用链的信息即可比较准确地定位出泄露代码的位置。若不存在泄露换句话说就是内存中的对象确实都还必须存活着那就应当检查VM的堆参数最小值-Xms和最大值-Xmx参数与机器物理内存对比看是否还可以调大从代码检查是否存在某些对象生命周期过长、持有状态时间过长的情况尝试减少程序运行期的内存消耗。 虚拟机栈和本地方法栈溢出 Hotspot VM并不区分 VM Stack和 Native Method Stack因此-Xoss参数设置本地方法栈大小形同虚设栈容量只由-Xss参数设定。 关于VM Stack和 Native Method Stack在JVM规范中描述了两种异常 若线程请求的栈深度大于VM所允许的最大深度将抛出StackOverflowError异常。若VM在扩展栈时无法申请到足够的内存空间则抛出OutOfMemoryError异常 这两异常有相互重叠地方当栈空间无法继续分配时到底是内存太小还是栈空间太大其本质上只是对同一件事情的两种描述。 在单线程环境操作 package com.lun.c02;/*** VM Args-Xss128k* author zzm*/ public class JavaVMStackSOF {private int stackLength 1;public void stackLeak() {stackLength;stackLeak();}public static void main(String[] args) throws Throwable {JavaVMStackSOF oom new JavaVMStackSOF();try {oom.stackLeak();} catch (Throwable e) {System.out.println(stack length: oom.stackLength);throw e;}} }/* stack length:999 Exception in thread main java.lang.StackOverflowErrorat com.lun.c02.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)at com.lun.c02.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)at com.lun.c02.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13) ... */ 使用 -Xss参数减少栈内存存量。会抛出java.lang.StackOverflowError异常出现时输出的堆栈深度相应缩小。定义大量的本地变量增大此方法帧中本地变量表的长度。会抛出java.lang.StackOverflowError异常出现时输出的堆栈深度相应缩小。 上例程序针对第1点 实验结果表明单线程环境下无论栈帧太大还是VM栈容量太小当内存无法分配时VM都抛出StackOverflowError异常等。 package com.lun.c02;/*** VM Args-Xss2M 这时候不妨设大些* author zzm*/ public class JavaVMStackOOM {private void dontStop() {while (true) {}}public void stackLeakByThread() {while (true) {Thread thread new Thread(new Runnable() {Overridepublic void run() {dontStop();}});thread.start();}}public static void main(String[] args) throws Throwable {JavaVMStackOOM oom new JavaVMStackOOM();oom.stackLeakByThread();} }把-Xss从108k设置到1024M 5min内 依然跑不出书上那样结果加上-Xmx20M -Xms20M 也是一样跑不出上例通过不断创建线程方式产生内存溢出异常。但是这样产生的内存溢出异常与栈空间是否足够大并存在任何联系或者准确地说在这种情况下为每个线程的栈分配的内存越大反而越容易产生内存溢出异常。为什么我跑不出这样的实验结果呢 归根到底OS分配每个进程内存是有限制的譬如32位的Windows限制为2GB。VM提供了参数来参加Java堆 和 方法区的这两部分内存的最大值。剩余内存为2GBOS限制减去Xmx最大堆容量再减去MaxPermSize最大方法区容量PCR内存消耗很小可忽略。 若VM进程本身消耗的内存不计算在内剩下的内存就由VM Stack 和 Native Method Stack“瓜分”了。 每个线程分配到的栈容量越大可建立的线程数量自然就越少建立线程时就越容易把剩下内存耗尽。 解决之道 在开发多线程时要注意出现StackOverflowError会有错误堆栈信息可阅读相对来说比较容易找打问题的所在。而且若使用虚拟机默认参数栈深度通常达到1000~2000完全没问题对于正常的方法调用包括递归这深度应该完全够用了。 但是若建立过多线程导致的内存溢出在不能减少线程数或者更换64位VM情况下就只能通过减少最大堆和减少栈容量来换取更多线程。 这种通过“减少内存”的手段来解决内存溢出的方式会比较难以想到。似非而是 方法区和运行时常量池溢出 运行时常量池溢出 JDK1.7开始逐步“去永久代”。 String.intern()是一个Native方法其作用若字符串常量池中已经包含一个等于此String对象的字符串则返回代表池中这个字符串的String对象否则将此String对象包含的字符串添加到常量池中并且返回此String对象的引用。 JDK1.6及以前的版本中由于常量池分配在永久代内可通过-XX:PermSize和-XX:MaxPermSize限制方法区大小从而间接限制其中常量池的容量-XX:PermSize 和 -XX:MaxPermSize 已被JDK1.8移除取而代之的是-XX:MetaspaceSize和-XX:MaxMetaspaceSize package com.lun.c02;import java.util.ArrayList; import java.util.List;/*** VM Args-XX:PermSize10M -XX:MaxPermSize10M* JDK1.8 : -XX:MetaspaceSize10M -XX:MaxMetaspaceSize10M* author zzm*/ public class RuntimeConstantPoolOOM {public static void main(String[] args) {// 使用List保持着常量池引用避免Full GC回收常量池行为ListString list new ArrayListString();// 10MB的PermSize在integer范围内足够产生OOM了int i 0; while (true) {list.add(String.valueOf(i).intern());}} }/* Error occurred during initialization of VM OutOfMemoryError: Metaspace *//* 设置类-Xms20M -Xmx20MException in thread main java.lang.OutOfMemoryError: GC overhead limit exceededat java.lang.Long.toString(Long.java:397)at java.lang.String.valueOf(String.java:3113)at com.lun.c02.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:20) Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize1k; support was removed in 8.0 Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize1k; support was removed in 8.0*/ 运行时常量属于方法区HotSpot虚拟机中永久代的一部分。 使用JDK1.7运行这段程序就不会得到相同的结果while循环将一直进行下去 JDK 1.8 JVM内部结构的元空间(Metaspace)取代永久代(PermGen) package com.lun.c02;public class RuntimeConstantPoolOOM2 {public static void main(String[] args) {String str1 new StringBuilder(中国).append(钓鱼岛).toString();System.out.println(str1.intern() str1);String str2 new StringBuilder(ja).append(va).toString();System.out.println(str2.intern() str2);} } 运行结果 JDK1.6 - 两个falseJDK1.7 - 前true后false 在JDK1.6intern()会把首先遇到的字符串实例复制到永久代中返回的也是永久代中这个字符串实例的引用而由StringBuilder创建的字符串实例在Java堆上所以必然不是同一个引用将返回false。 在JDK1.7的intern()实现不会再复制实例只是在常量池中记录首次出现的实例引用因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。 对str2比较返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过字符串常量池中已经有它的引用不符合“首次出现”的原则而“计算机软件”这个字符串是首次出现因此返回true 方法区溢出 方法区用于存放Class的相关信息如 类名访问修饰符常量池字段描述方法描述。。。 对于这区域测试基本思路是运行时产生大量的类填满方法区直到溢出。 为简单起见不使用反射而借助CGLib直接操作字节码运行时生成了大量的动态类。 首先添加CGLib依赖 !-- https://mvnrepository.com/artifact/cglib/cglib -- dependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.2.10/version /dependencypackage com.lun.c02;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;/*** VM Args -XX:PermSize10M -XX:MaxPermSize10M* * JDK1.8 VM Args:-XX:MetaspaceSize10M -XX:MaxMetaspaceSize10M* * * author zzm*/ public class JavaMethodAreaOOM {public static void main(String[] args) {while (true) {Enhancer enhancer new Enhancer();enhancer.setSuperclass(OOMObject.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {Overridepublic Object intercept(Object obj, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {return proxy.invokeSuper(obj, args);}});enhancer.create();}}static class OOMObject {} }/*JDK1.8 result Exception in thread main net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException--nullat net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:348)at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:117)at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)at com.lun.c02.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:27) Caused by: java.lang.reflect.InvocationTargetExceptionat sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:459)at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)... 6 more Caused by: java.lang.OutOfMemoryError: Metaspaceat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:763)... 11 more*/ 当前的很多主流框架如Spring、Hibernate在对此进行增强时都会使用CGLib这类字节码技术增强的类越多就需越大的方法区来保证动态生成的Class可以加载入内存。 另外JVM动态语言如Groovy等通常都会持续创建类来实现语言的动态性。 一个类要被GC回收掉判定条件是比较苛刻的。在经常动态生成大量Class的应用中需要特别注意类的回收状况。 这类场景除了上面提到的程序使用了CGLib字节码增强和动态语言之外常见还有 大量JSP或动态产生的引用JSP第一次运行时需要编译为Java类基于OSGi的应用即使是同一个类文件,被不同的加载器加载也会视为不同的类 方法区和运行时常量池溢出小结 -XX:PermSize 和 -XX:MaxPermSize 已被JDK1.8移除查阅资料得知JDK 1.8 JVM内部结构的元空间(Metaspace)取代永久代(PermGen)所以在JDK1.8 机器上跑不出书本上那样结果。 若把参数换成-XX:MetaspaceSize和-XX:MaxMetaspaceSize则有类似的结果。 本机直接内存溢出 DirectMemory容量可通过-XX:MaxDirectMemorySize指定若不指定则默认与Java堆最大值-Xmx指定一样。 直接通过Unsafe实例进行内存分配Unsafe类的getUnsafe()方法限制了只有引导类加载器才会返回实例也就是设计者希望只有rt.jar中的类才能使用Unsafe的功能。 DirectByteBuffer分配内存也会抛出内存溢出异常但它抛出异常时并没有真正想OS申请分配内存而是通过计算得知内存无法分配于是直接抛出异常真正申请分配内存的方法是unsafe.allocateMemory() package com.lun.c02;import java.lang.reflect.Field; import sun.misc.Unsafe;/*** VM Args-Xmx20M -XX:MaxDirectMemorySize10M* author zzm*/ 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 com.lun.c02.DirectMemoryOOM.main(DirectMemoryOOM.java:18) */由DirectMemory导致的内存溢出一个明显的特征是在Heap Dump文件中不会看见明显的异常若发现OOME之后Dump文件很小而程序中又直接或间接使用了NIO那就可以考虑检查一下是不是这方面的原因。
http://www.pierceye.com/news/600361/

相关文章:

  • 建一个网站怎么赚钱吗家具网站源码
  • 云优化网站建设wordpress开启icon
  • 招聘网站开发的目的与意义农特产品电商网站建设目标
  • 三水 网站建设公司企业黄页
  • 网站建设公司词辽宁阜新建设学校官方网站
  • 广州公司网站建设设计顾视频网站的建设预算
  • 商务网站规划与网页制作seo优化内容
  • 石家庄网站定做公众号开发单位
  • 做预定网站的作用网站建设需求方案文档
  • 西安网站建设高端万网总裁张向东
  • 肖鸿昌建筑网站广州网站建设设计公司信息
  • 网站建设 大公司好成都网站建设哪家售后好
  • 外贸网站模板制作微营销推广方案
  • 网站开发体系用node.js可以做网站吗
  • 一个vps建两个网站怎么弄数据库网络营销应用方式
  • 网站开发快递c 网站开发入门视频教程
  • 阿里巴巴国际站介绍深圳网站建设 猴王网络
  • 扬中网站建设哪家好五百丁简历官网
  • 素马设计顾问讲解价格短视频seo什么意思
  • 注册域名查询网站智慧团建网站登陆平台
  • 网站建设和搜索引擎优化技术有哪些
  • 网站创建的基本流程seo网站排名全选
  • 乐山网站公众号建设wordpress微电影模板
  • 天津专门做网站长春市网站制作
  • 怎样用php做网站英文网站建设580
  • 凡客登录入口网站优化垂直化好还是扁平化好
  • 网站诊断高端网站建设 杭州
  • 营销网站案例北京公司建设网站
  • 网站建设全域云网店美工的职责有哪些
  • seo优化网站的手段亚洲国产中文域名查询