1 jvm内存模型

1.1 jvm内存区域划分

大多数 JVM 将内存区域划分为 Method Area(方法区),Heap(堆) , Program Counter Register(程序计数器) , VM Stack(虚拟机栈),Native Method Stack ( 本地方法栈 ),其中Method AreaHeap 是线程共享的 ,VM StackNative Method StackProgram Counter Register 是非线程共享的。为什么分为线程共享和非线程共享的呢?

首先我们熟悉一下一般性的 Java 程序的工作过程。一个 Java 源程序文件,会被编译为字节码文件(以 class 为扩展名),每个java程序都需要运行在自己的JVM上,然后告知 JVM 程序的运行入口,再被 JVM 通过字节码解释器加载运行。那么程序开始运行后,都是如何涉及到各内存区域的呢?

概括地说来,JVM初始运行的时候都会分配好 Method Area(方法区)Heap(堆),而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说只发生在Heap上)的原因。

jvm内存模型

1.2 各区域介绍

1.2.1 Heap(堆)

所有的对象和数组都存放在这里,jdk1.8后,字符串常量池从永久代中剥离出来,存放在堆中。堆又细分为年轻代(Young)和年老代(OldGen)。

  • 年轻代(Young)
    Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区,其中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到OldGen区间。
  • 年老代(OldGen)
    OldGen区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到OldGen区

1.2.2 Method Area(方法区)

方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域。在1.8后,使用了元数据来实现方法区,元数据空间直接存放到本地内存中的,与jvm内存无关.

jdk1.8堆内存模型

1.2.3 Program Counter Register(程序计数器)

每个线程一块,指向当前线程正在执行的字节码代码的行号。如果当前线程执行的是native方法,则其值为null。

1.2.4 VM Stack(虚拟机栈)

线程私有,生命周期与线程同进同退。每个Java方法在被调用的时候都会创建一个栈帧,并入栈。一旦完成调用,则出栈。所有的的栈帧都出栈后,线程也就完成了使命。

1.2.5 Native Method Stack ( 本地方法栈 )

功能与Java虚拟机栈十分相同。区别在于,本地方法栈为虚拟机使用到的native方法服务。

2 jvm运行参数

2.1 三种参数类型

  • 标准参数
    • -help
    • -version
  • -X参数(非标准参数)
    • -Xint
    • -Xcomp
  • -XX参数(使用率比较高)

2.2 标准参数

jvm的标准参数,一般都是很稳定的,在未来的JVM版本中不会改变,可以使用java -help检索出所有的标准参数。

2.2.1 -server与-client参数

可以通过-server或-client设置jvm的运行参数。

  • 它们的区别是Server VM的初始堆空间会大一些,默认使用的是并行垃圾回收器,启动慢运行快。
  • Client VM相对来讲会保守一些,初始堆空间会小一些,使用串行的垃圾回收器,它的目标是为了让JVM的启动速度更快,但运行速度会比Serverm模式慢些。
  • JVM在启动的时候会根据硬件和操作系统自动选择使用Server还是Client类型的JVM。
  • 32位操作系统
    • 如果是Windows系统,不论硬件配置如何,都默认使用Client类型的JVM。
    • 如果是其他操作系统上,机器配置有2GB以上的内存同时有2个以上CPU的话默认使用server模式,否则使用client模式。
  • 64位操作系统
    • 只有server类型,不支持client类型。

2.3 -X参数

jvm的-X参数是非标准参数,在不同版本的jvm中,参数可能会有所不同,可以通过java -X查看非标准参数。

2.3.1 -Xint、-Xcomp、-Xmixed

  • 在解释模式(interpreted mode)下,-Xint标记会强制JVM执行所有的字节码,当然这会降低运行速度,通常低
    10倍或更多。
  • -Xcomp参数与它(-Xint)正好相反,JVM在第一次使用时会把所有的字节码编译成本地代码,从而带来最大程度的优化。
    • 然而,很多应用在使用-Xcomp也会有一些性能损失,当然这比使用-Xint损失的少,原因是-xcomp没有让JVM启用JIT编译器的全部功能。JIT编译器可以对是否需要编译做判断,如果所有代码都进行编译的话,对于一些只执行一次的代码就没有意义了。
  • -Xmixed是混合模式,将解释模式与编译模式进行混合使用,由jvm自己决定,这是jvm默认的模式,也是推荐使用的模式。

2.4 -XX参数

-XX参数也是非标准参数,主要用于jvm的调优和debug操作。
-XX参数的使用有2种方式,一种是boolean类型,一种是非boolean类型:

  • boolean类型
    格式:-XX:[+-] 表示启用或禁用属性
    如:-XX:+DisableExplicitGC 表示禁用手动调用gc操作,也就是说调用System.gc()无效
  • 非boolean类型
    格式:-XX:= 表示属性的值为
    如:-XX:NewRatio=1 表示新生代和老年代的比值

2.5 -Xms与-Xmx参数

-Xms与-Xmx分别是设置jvm的堆内存的初始大小和最大大小。
-Xmx2048m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为2048M。
-Xms512m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为512M。
适当的调整jvm的内存大小,可以充分利用服务器资源,让程序跑的更快。

2.6 查看jvm运行参数

2.6.1 运行java命令时打印参数

运行java命令时打印参数,需要添加-XX:+PrintFlagsFinal参数即可。

java -XX:+PrintFlagsFinal -version

2.6.2 查看正在运行的jvm参数

1、先用jps -l查看Java进程
2、使用jinfo -flags <进程id>查看运行参数

Q.E.D.


愿你编码半生,归来仍是少年