JVM内存结构

1、为何要了解JVM内存管理机制

  • JVM自动的管理内存的分配与回收,这会在不知不觉中浪费不少内存,致使JVM花费不少时间去进行垃圾回收(GC)
  • 内存泄露,致使JVM内存最终不够用

2、认识JVM基本结构

线程私有:html

  一、程序计数器:前端

  程序计数器是一个较小的内存空间,它能够被当作当前线程所执行的字节码的行号指示器。程序计算器保证了线程切换后能恢复到正确的执行位置。 若是线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;若是正在执行的是Natvie方法,这个计数器值则为空(Undefined)。在这里,程序计数器是惟一一个在Java虚拟机规范中没有规定OutOfMemoryError状况的区域。java

  二、Java栈(全称是java虚拟机栈):数组

  虚拟机栈栈描述的是Java方法执行的内存模型,每一个方法在执行的同时都会建立一个栈帧,用于存储局部变量表、操做数栈,动态连接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈出栈的过程。服务器

       局部变量表:在变量槽中保存方法的参数和变量(即各类基本数据类型、对象引用类型),每一个变量槽 4 个字节占 32 位,能够是基本数据类型也能够是对象引用。局部变量表大小在编译时就已经肯定,long 和 double 类型数据会占 2 个变量槽,其他的只占用 1 个数据结构

  操做数栈:在执行指令的时候用于数据的存取,其大小也是在编译时就已经肯定jvm

  动态连接:每一个栈帧都有它在运行时常量池中所属方法的引用,在运行时会转换为直接引用jsp

  返回地址:当一个方法开始执行后,有两种方式能够退出这个方法:一是执行引擎遇到返回的字节码指令;另外一种是在方法执行中出现了异常而且没有在此方法中处理,这种退出不会返回给上层调用者任何值。不管如何退出,都须要继续执行上层方法。在方法退出时可能的执行操做有:把返回值(若是有)压入上层方法的栈帧的操做数栈中,调整 PC 寄存器指向方法调用指令的后一条指令。ui

      调节参数lua

  • -Xss:设置栈的大小,一般设置为1m就好
  • <jvm-arg>-Xss1m</jvm-arg>

       在Java虚拟机规范中,对这个区域规定了两种异常情况:若是线程请求的栈深度大于虚拟机所容许的深度,将抛出StackOverflowError异常若是虚拟机栈能够动态扩展(当前大部分的Java虚拟机均可动态扩展,只不过Java虚拟机规范中也容许固定长度的虚拟机栈),当扩展时没法申请到足够的内存时会抛出OutOfMemoryError异常。

  三、本地方法方法栈:

  本地方法栈(Native Method Stacks)与虚拟机栈所发挥的做用是很是类似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并无强制规定,所以具体的虚拟机能够自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈同样,本地方法栈区域也会抛StackOverflowError和OutOfMemoryError异常。

线程共享:

  一、Java堆:

  Java堆是被全部线程共享的一块内存区域,在虚拟机启动时建立

  Java堆是垃圾收集器管理的主要区域,所以不少时候也被称作“GC堆”。

  Java堆能够处于物理上不连续的内存空间中,只要逻辑上是连续的便可,就像咱们的磁盘空间同样。在实现时,既能够实现成固定大小的,也能够是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(经过-Xmx和-Xms控制)。

  • 存放内容
    • 对象实例(类中的实例变量会随着对象实例的建立一块儿分配在堆中,固然如果基本数据类型的话,会随着对象的建立直接压入操做数栈)
    • 数组值
  • 使用实例
    • 全部经过new建立的对象都在这块儿内存分配,具体分配到年轻代仍是年老代须要根据配置参数而定(新建对象直接分配到年老代有两种状况,看下边)
  • 调节参数
    • -Xmx:最大堆内存,默认为物理内存的1/4但小于1G
    • -Xms:最小堆内存,默认为物理内存的1/64但小于1G
    • -XX:MinHeapFreeRatio,默认当空余堆内存小于最小堆内存的40%时,堆内存增大到-Xmx
    • -XX:MaxHeapFreeRatio,当空余堆内存大于最大堆内存的70%时,堆内存减少到-Xms
  • 注意点
    • 在实际使用中,-Xmx与-Xms配置成相等的,这样,堆内存就不会频繁的进行调整了
  • 抛出错误
    • OutOfMemoryError:在堆中没有内存完成实例分配(关于实例内存的分配,以后再说),此时堆内存已达到最大没法扩展时。
  • 堆内存划分

    • 新生代
      • 组成:Eden+From(S0)+To(S1)
      • -Xmn:整个新生代的大小
      • -XX:SurvivorRatio:调整Eden:From(To)的比率,默认为8:1
    • 年老代
      • 新建对象直接分配到年老代,两种状况
        • 大对象:-XX:PretenureSizeThreshold(单位:字节)参数来指定大对象的标准,在Parallel Scavenge GC下可能无效
        • 大数组:数组中的元素没有引用任何外部的对象
  • 总结
    • 企业开发中,-Xmx==-Xms
    • 一般,-Xmx设置为2048m就没问题了,固然还要根据本身的程序去预估,并在运行过程当中去调整,这里以在Resin服务器中配置为例
    •             <jvm-arg>-Xms2048m</jvm-arg>
                  <jvm-arg>-Xmx2048m</jvm-arg>
                  <jvm-arg>-Xmn512m</jvm-arg>
                  <jvm-arg>-XX:SurvivorRatio=8</jvm-arg>
                  <jvm-arg>-XX:MaxTenuringThreshold=15</jvm-arg>        

       


      能够看到,-Xms==-Xmx==2048m,年轻代大小-Xmn==512m,这样,年老代大小就是2048-512==1536m,这个比率值得记住,在企业开发中,年轻代:年老代==1:3,而此时,咱们配置的-XX:MaxTenuringThreshold=15(这也是默认值),年轻代对象通过15次的复制后进入到年老代(关于这一点,在以后的GC机制中会说),

    • -XX:MaxTenuringThreshold与-XX:PretenureSizeThreshold不同,不要看错

  二、方法区:

   方法区(Method Area)与Java堆同样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,可是它却有一个别名叫作Non-Heap(非堆),目的应该是与Java堆区分开来。

  • 存放内容(类的信息、类static属性、方法、常量池)
    • 已经加载的类的信息(名称、修饰符等)
    • 类中的static变量
    • 类中的field信息
    • 类中定义为final常量
    • 类中的方法信息
    • 运行时常量池:编译器生成的各类字面量和符号引用(编译期)存储在class文件的常量池中,这部份内容会在类加载以后进入运行时常量池

     运行时常量池是方法区一个很是重要的区域,简称 RCP。首先咱们要知道在字节码文件中,除了有类的字段、方法等信息描述外,还有常量池信息。常量池用来保存常量(字符串常量和 final 常量)与符号引用,这部份内容在被类加载后,会储存到方法区中的 RCP 中,能够说 RCP 是类中的常量池在运行时的表示形式。另外运行时产生的常量也能够被放入常量池中,好比 String 的 intern() 方法,当常量池扩展时没法申请到内存时会抛出 OutOfMemoryError 异常。

  • 使用实例:反射,在程序中经过Class对象调用getName等方法获取信息数据时,这些信息数据来源于方法区。
  • 调节参数
    • -XX:PermSize:指定方法区的最小值,默认为16M
    • -XX:MaxPermSize:指定方法区的最大值,默认为64M
  • 所抛错误
    • 方法区域要使用的内存超过了其容许的大小时,抛出OutOfMemoryError
  • 内存回收的主要目标
    • 对类的卸载(这也是为何不少企业使用velocity等模板引擎作前端而不是使用jsp的缘由之一)
    • 针对常量池的回收
  • 总结
    • 通常而言,在企业开发中,-XX:PermSize==-XX:MaxPermSize
    • 一般,这个大小设置为256M就没问题了,固然还要根据本身的程序去预估,并在运行过程当中去调整,这里以在Resin服务器中配置为例
    •  <jvm-arg>-XX:PermSize=256M</jvm-arg>
       <jvm-arg>-XX:MaxPermSize=256M</jvm-arg>
    •  类中的static变量会在方法区分配内存,可是类中的实例变量不会(类中的实例变量会随着对象实例的建立一块儿分配在堆中,固然如果基本数据类型的话,会随着对象的建立直接压入操做数栈)
    • 关于方法区的存放内容,能够这样去想全部的经过Class对象能够反射获取的都是从方法区获取的(包括Class对象也是方法区的,Class是该类下全部其余信息的访问入口)  

注意:常量池在jdk1.6在方法区;在jdk1.7在堆

附:元数据区

  • 调节参数:-XX:MaxMetaspaceSize,若是不指定大小,极限状况下可能耗尽系统全部内存
  • 元数据区是堆外的一块直接内存

 

    备注: 部份内容转自 https://blog.csdn.net/luanlouis/article/details/40043991   http://www.cnblogs.com/java-zhao/p/5179836.html

相关文章
相关标签/搜索