zoukankan      html  css  js  c++  java
  • JVM内存结构

    java 虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统
     
     
    JVM Heap 分两大块:  一块是 NEW Generation,另一块是 Old Generation.
    在 NewGeneration 中,有一个叫 Eden 的空间,主要是用来存放新生的对象,还有两个 Survivor Spaces(from,to),它们的大小总是一样,它们用来存放每次垃圾回收后存活下来的对象。
    在 OldGeneration 中,主要存放应用程序中生命周期长的内存对象。
    在NewGeneration 块中,垃圾回收一般用 Copying 的算法,速度快。每次 GC 的时候,存活下来的对象首先由 Eden 拷贝到某个 SurvivorSpace, 当 Survivor Space 空间满了后, 剩下的 live 对象就被直接拷贝到OldGeneration 中去。因此,每次 GC 后,Eden 内存块会被清空。
    在 OldGeneration 块中,垃圾回收一般用 mark-compact 的算法,速度慢些,但减少内存要求.
     
    垃圾回收分多级:
    Full GC:会回收 OLD 和 Perm中的垃圾;
    Major GC:会回收OLD 中的垃圾;
    Minor GC:只回收 NEW 中的垃圾;
    内存溢出通常发生于 OLD 段或 Perm 段垃圾回收后,仍然无内存空间容纳新的 Java对象的情况。 
     
    内存申请过程如下:
    1. JVM 会试图为相关 Java 对象在 Eden 中初始化一块内存区域
    2. 当 Eden 空间足够时,内存申请结束。否则到下一步
    3. JVM 试图释放在 Eden 中所有不活跃的对象(这属于minor级别垃圾回收),释放后若 Eden 空间仍然不足以放入新对象,则试图将部分 Eden 中活跃对象放入 Survivor 区
    4. Survivor 区被用来作为 Eden 及 OLD 的中间交换区域,当to空间放不下eden和from的对象,且OLD 区空间担保成功时,from区的对象会被移到 Old 区,否则会被移到to 区
    5. 当 OLD 区空间不够时,JVM 会在 OLD 区进行full GC级
    6. 完全垃圾收集后,若 to 及 OLD 区仍然无法存放从 Eden 复制过来的部分对象,导致 JVM无法在 Eden 区为新对象创建内存区域,则出现”out of memory 错误”
     
    hotspot jvm结构如下(虚拟机栈和本地方法栈合一起了):

    JDK8 永久代变化如下图:

    1.新生代:Eden+From Survivor+To Survivor

    2.老年代:OldGen

    3.永久代(方法区的实现) : PermGen----->替换为Metaspace(本地内存中)

    为什么废弃永久代:由于永久代内存经常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen

    深入理解元空间(Metaspace):元空间的内存大小:元空间并不在虚拟机中,而是使用本地内存。理论上取决于32位/64位系统可虚拟的内存大小。可见也不是无限制的,需要配置参数。

    在jdk8中:

    1.字符串常量由永久代转移到堆中。

    2.持久代已不存在,PermSize MaxPermSize参数已移除。

    3.类加载(方法区的功能)已经不在永久代PerGem中了,而是Metaspace中

      
    VM 调优建议:
    在用户生产环境上一般将以下三组值设为相同,以减少运行期间系统在内存申请上所花的开销。
    1.ms/mx:定义 YOUNG+OLD 段的总尺寸,ms 为 JVM 启动时 YOUNG+OLD 的内存大小;mx 为最大可占用的 YOUNG+OLD 内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。(进程启动时申请ms内存,当运行过程中需要的内容超过ms时,进程需要挂起来等待申请到更新内存时才能执行。为了避免这种情况发生,最好定义ms和mx相同)
    2.NewSize/MaxNewSize:定义 YOUNG 段的尺寸,NewSize 为 JVM 启动时 YOUNG 的内存大小;MaxNewSize 为最大可占用的 YOUNG 内存大小。
    3.由于heap大小值相同,Yound大小值相同、所以剩下的Old大小值应该也相同
     
     
    1. OLD 段溢出
    这种内存溢出是最常见的情况之一,产生的原因可能是:
    1) 设置的内存参数过小(ms/mx, NewSize/MaxNewSize)
    2) 程序问题
    单个程序持续进行消耗内存的处理,如循环几千次的字符串处理,对字符串处理应建议使用 StringBuilder。此时不会报内存溢出错,却会使系统持续垃圾收集,无法处理其它请求,相关问题程序可通过 Thread Dump获取单个程序所申请内存过大,有的程序会申请几十乃至几百兆内存,此时 JVM也会因无法申请到资源而出现内存溢出,对此首先要找到相关功能,然后交予程序员修改,要找到相关程序,必须在 Apache 日志中寻找。当 Java 对象使用完毕后,其所引用的对象却没有销毁,使得 JVM 认为他还是活跃的对象而不进行回收,这样累计占用了大量内存而无法释放。
    2. Perm 段溢出
    通常由于 Perm 段装载了大量的 Servlet 类而导致溢出,目前的解决办法:
    1) 将 PermSize 扩大,一般 256M 能够满足要求
    2) 若别无选择,则只能将 servlet 的路径加到 CLASSPATH 中,但一般不建议这么处理
     
     
    3.  Heap 溢出
    系统对 Heap 没有限制,故  Heap 发生问题时,Java 进程所占内存会持续增长,直到占用所有可用系统内存
     
    4.其他:
    JVM 有 2 个 GC 线程。第一个线程负责回收 Heap 的 Young 区。第二个线程在 Heap 不足时,遍历 Heap,将 Young 区升级为 Older 区。Older 区的大小等于-Xmx 减去-Xmn,不能将-Xms 的值设的过大,因为第二个线程被迫运行会降低 JVM 的性能。
     
    为什么一些程序频繁发生 GC?有如下原因:
     程序内调用了 System.gc()或 Runtime.gc()。 一些中间件软件调用自己的 GC 方法,此时需要设置参数禁止这些 GC。
     Java 的 Heap 太小,一般默认的 Heap 值都很小。
     频繁实例化对象,Release 对象。此时尽量保存并重用对象,例如使用 StringBuffer()和 String()。
    如果你发现每次 GC 后,Heap 的剩余空间会是总空间的 50%,这表示你的 Heap 处于健康状态。许多 Server端的 Java 程序每次 GC 后最好能有 65%的剩余空间。
     
    典型设置:
    • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
      -Xmx3550m:设置JVM最大可用内存为3550M。
      -Xms3550m:设置JVM初始内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
      -Xmn2g:设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小(jdk8以后废弃)。
      -Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
    • java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
      -XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
      -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
    2.增加 Heap 的大小虽然会降低 GC 的频率,但也增加了每次 GC 的时间。并且 GC 运行时,所有的用户线程将暂停,也就是 GC 期间,Java 应用程序不做任何工作。
    4.Heap 大小并不决定进程的内存使用量。进程的内存使用量要大于-Xmx 定义的值,因为 Java 为其他任务分配内存,例如每个线程的 Stack 等。
    5 .  ystem.gc()显示调用会触发 Full GC。对整个堆进行整理,包括 Young、Tenured 和 Perm。要尽量避免。
     
    进一步调优总结,可参考http://unixboy.iteye.com/blog/174173/
     
     
  • 相关阅读:
    View 和 ViewGroup 的 hasFocusable
    tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断【转】
    tiny4412学习(三)之移植linux-4.x驱动(1)支持网卡驱动【转】
    分享tiny4412,emmc烧录u-boot, 支持fastboot模式烧写emmc【转】
    tiny4412学习(一)之从零搭建linux系统(烧写uboot、内核进emmc+uboot启动内核)【转】
    【总结】嵌入式linux内核中Makefile、Kconfig、.config的关系及增加开机Hello World【转】
    tiny4412 裸机程序 九、串口排查驱动原因及字符图片显示【转】
    tiny4412 裸机程序 八、重定位到DRAM及LCD实验【转】
    tiny4412 裸机程序 七、重定位代码到DRAM【转】
    tiny4412 裸机程序 六、重定位代码到IRAM+0x8000【转】
  • 原文地址:https://www.cnblogs.com/wzj4858/p/8206645.html
Copyright © 2011-2022 走看看