JVM相关内容

JVM:

作用:

从软件层面屏蔽不同操作系统在底层硬件与指令上的区别

组成:

运行时数据区域+类装载子系统(加载字节码文件到方法区当中)+执行引擎

JVM内存划分:

  1. 栈(stack):存放的都是方法中的局部变量,方法的运行一定要在栈当中
    • 局部变量:方法的参数,或者是方法内部的变量
    • 作用域:一旦超出范围,立即从栈内存当中消失
    • 当程序运行过程中有一个线程运行时,JVM会在内存当中给这个线程分配出一块内存空间,这块儿内存空间就是栈,这个线程运行过程当中运行到的每一个方法都会进入这个栈,并且Jvm会给这个这个方法在当前这个线程的栈空间中分配一块儿栈内存空间,这一块儿内存空间就被称为栈帧,栈帧中存放的就是当前方法的局部变量表(用来存放当前方法的局部变量),操作数栈(用来临时存放程序运行过程中的数据),动态链接(在程序运行的过程中把符号引用,转变为符号对应的指令码的直接引用地址)和方法出口(保存现场的作用,里面保存的是当前方法执行完要回到什么位置),当这个方法运行结束后,这个栈帧也就出栈了
  2. 堆(heap):凡是new出来的东西,都在堆当中,这个对象的引用存放在栈当中
    • 堆内存里面的东西都有一个地址值
    • 堆内存里面的数据,都有默认值,规则:
      • 如果是整数 默认为0;
      • 如果是浮点数 默认为0.0;
      • 如果是字符 默认为’\u0000’
      • 如果是布尔 默认为false
      • 如果是引用类型 默认为null
    • new出来的对象最先存放在Eden区,当Eden区被不断生成的对象占满时,会执行minorGC(垃圾收集),收集堆中游离的对象(没有GC Roots指针指向的对象),并将存活的对象存放到from区,当from区也满了之后,也会触发JVM执行引擎的垃圾收集(minorGC),并将存活的对象放到To区(并将分代年龄加一),当To区又被放满时,JVM又会执行(minorGC),将存活的对象存放到from区(并将分代年龄加一),默认当分代年龄为15时,如果这个对象还没有被销毁,就会将该对象放到老年代中,当老年代放满之后会执行(fullGC)垃圾收集,当垃圾收集没有从老年代中收集到什么对象时(老年代没有空间了),会产生OutOfMemoryError错误
    • GC Roots根节点:类加载器,Thread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等
    • finalize()方法最终判定对象是否存活:
      • 即使在可达性分析算法中不可到达的对象,也并非是”非死不可” 的,这时候它们暂时存储于缓刑阶段,要真正宣告一个对象死亡,至少要经过两次标记过程,标记的前提是对象在进行可达性分析后,发现没有与GC Roots相链接的引用链
      • 第一次标记进行一次筛选:筛选的条件是此对象是否有必要执行finalize()方法,当对象没有覆盖finalize方法,或者finalize方法已经被虚拟机调用过了,虚拟机将这两种情况都视为没有必要执行finalize()方法,对象被回收
      • 第二次标记,如果这个对象被判定为有必要执行finalize()方法,那么这个对象会被放置在一个队列中,并在稍后由一条虚拟机自动建立的,低优先级的finalizer线程中去执行,finalize方法是对象脱离死亡命运的最后一次机会,稍后GC将对队列中的对象进行第二次小规模标记,如果对象在finalize()中自救成功——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己的值赋给某个类变量或对象的成员变量,那么在第二次标记时它将被移出”即将被回收”的集合,如果对象没有逃脱,那么它就真的被回收了
    • finalize 的主要目的是在不可撤消地丢弃对象之前执行清除操作。
  3. 方法区(元空间Method Area):存储.class相关的信息,包含方法的信息,用来存储常量和静态变量以及类元信息使用直接内存,并不占用虚拟机的内存
  4. 本地方法栈(Native Method Stack):也是每个线程独有的,java代码在执行系统方法的时候,系统方法中也有一些局部变量,这些变量就存储在本地方法栈当中
  5. 程序计数器(pc Register):与CPU相关,是每一个线程独有的,每一个线程在运行过程中,jvm都会给该线程分配一个程序计数器,用来标识该线程马上要执行的那一行代码的内存地址指针
  6. 常量池:它是方法区的一部分。Class文件中除了有类的版本信息、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译期间生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中,另外翻译出来的直接引用也会存储在这个区域中。这个区域另外一个特点就是动态性,Java并不要求常量就一定要在编译期间才能产生,运行期间也可以在这个区域放入新的内容,String.intern()方法就是这个特性的应用。

JVM参数设置:

-Xms:JVM启动时申请的初始Heap值,默认为操作系统物理内存的1/64但小于1G。

-Xmn: Java Heap Young区大小。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小(相对于HotSpot 类型的虚拟机来说)。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值

-Xmx: JVM可申请的最大Heap值,默认值为物理内存的1/4但小于1G,默认当空余堆内存小于40%时,JVM会增大Heap到-Xmx指定的大小,可通过-XX:MinHeapFreeRation=来指定这个比列。最佳设值应该视物理内存大小及计算机内其他内存开销而定。(例如:-Xmx4g)

-Xss : Java每个线程的Stack大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

-XX:PermSize: 持久代(方法区)的初始内存大小。(例如:-XX:PermSize=64m)

-XX:MaxPermSize: 持久代(方法区)的最大内存大小。(例如:-XX:MaxPermSize=512m)

Thanks!