zoukankan      html  css  js  c++  java
  • 浅入理解JVM

    JVM全称java虚拟机。HelloWorld.java(源代码) ->Hello.class->010101(2进制编码),class文件和2进制编码需要在JVM中运行。

    jvm运行时的数据区:

    蓝色的是数据区,绿色的是指令区。

    程序计数器:一块较小的内存空间,指向当前线程所执行的字节码指令的地址和行号,每条线程都有一个独立的程序计数器。唯一不会OOM的区域。

    虚拟机栈:线程私有,存储当前运行方法所需要的数据、指令、返回地址。

    每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接(比如存储的一个接口,运行时会解析找到实现类)、方法出口等信息。一个方法对应一个栈帧。局部变量表存放了各种基本类型、对象引用和returnAddress类型(指向了一条字节码指令地址)。其中64位长度long 和 double占两个局部变量空间,其他只占一个。可能出现2种异常:1.线程请求的栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;2.如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就抛出OutOfMemoryError异常。设置JVM参数”-Xss228k”(即栈大小为228k)。

     本地方法栈:和Java虚拟机栈很类似,不同的是本地方法栈为Native方法服务。在HotSpot虚拟机实现中是把本地方法栈和虚拟机栈合二为一的。同理也会抛出StackOverflowError和OutOfMemoryError。

    Java堆:是Java虚拟机所管理的内存中最大的一块。由所有线程共享,在虚拟机启动时创建。堆区唯一目的就是存放对象实例。堆中可细分为新生代和老年代,新生代又可分为Eden空间、From Survivor空间、To Survivor空间(8:1:1)。堆无法扩展时,抛出OutOfMemoryError异常。设置JVM参数” -Xms20M -Xmx20M“(前者表示初始堆大小20M,后者表示最大堆大小20M)。

    方法区:所有线程共享,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。当方法区无法满足内存分配需求时,抛出OutOfMemoryError。

    另外一个说法——永久代(Permanent Generation),呼应堆的新生代和老年代。方法区和堆的划分是JVM规范的定义,而不同虚拟机有不同实现,对于Hotspot虚拟机来说,将方法区纳入GC管理范围,这样就不必单独管理方法区的内存,所以就有了”永久代“这么一说。设置JVM参数为”-XX:MaxPermSize=20M”(方法区最大内存为20M)。

     在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代

    在JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代

    在JDK1.8 hotspot移除了永久代,取而代之的是”元空间(Metaspace)“,所以在JDK8中虚拟机参数”-XX:MaxPermSize”也就没有了任何意义,取代它的是”-XX:MetaspaceSize“和”-XX:MaxMetaspaceSize”等, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace) 。

    直接内存:并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。JDK1.4加入了NIO,引入一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。因为避免了在Java堆和Native堆中来回复制数据,提高了性能。当各个内存区域总和大于物理内存限制,抛出OutOfMemoryError异常。

    Question:  为什么堆的新生代三个区域的大小比为8:1:1?

    因为新生代使用复制回收算法。每次用来存放对象的是Eden区和其中一块Survivor区。当回收时,将Eden区和Survivor from中还存活着的对象一次性复制到另一块Survivor to区,复制算法所需要的担保内存为9:1。之所以Eden区:Survivor from区是8:1,是因为两个Survivor区的from和to区会进行位置交换。MinorGC后哪个区被清空没有对象了,这个区就会成为to区,而通过复制算法复制的还存活下的对象所在的那个区,也就是有对象的区即为from。(PS:新生代中Survivor to区内存不够用时,会触发老年代的担保机制进行分配担保。新生代与老年代默认内存比为1:2)

    总结一下就是:

    • 用来担保复制回收算法执行的内存为9:1。(留出一片空地)
    • S1:S2应为1:1,可用内存中Eden:S1区为8:1。所以新生代三个区比为8:1:1。(使用的内存中还有一块要成为下次的空地)

    JVM基础的参数和指令

    jvm三种参数类型:

    • 标配参数(java -version等)
    • X参数(-Xint解释执行,-Xcomp第一次使用就编译成本地代码,-Xmixed混合模式先编译再执行)
    • XX参数(分为Boolean和KV类型)

    jvm常见命令和参数:

    java -XX:+PrintFlagsInitial   ——查看所有默认参数(:=说明被人工修改)

    jps -l       ——查看正在运行中的进程号

    jinfo -flag 参数 进程号    ——查看某进程时候开启某布尔型参数

    jinfo -flags 进程号   ——查询系统参数(Non-defauls)和人工参数(Command line)

    -XX:+Boolean参数      ——表示开启某布尔型参数

    -XX:-Boolean参数      ——表示关闭某布尔型参数

    -XX:KV参数=???      ——表示设置KV类型参数为??

    from ??? to ???       ——表示从初始默认值到自我期望值(最好一致)

    -Xss: 初始栈内存大小 (等价于-XX:ThreadStackSize)

    -Xms: 初始堆内存大小 -Xmx:最大堆内存大小(等价于InitialHeapSize 和XX:MaxHeapSize )

    java -XX:+PrintCommandLineFlags -version  ——一种查看配置的方式,可以查看默认使用哪种垃圾回收器

    -XX:SurvivorRatio  ——调整新生代eden区对from区和to区的倍数,默认为8:1:1(例如设置SurvivorRatio=6,比例为6:1:1)

    -XX: NewRatio ——调整堆中老年代对新生代的占比,默认为2:1(例如设置为NewRatio=3,比例为3:1)

    -XX:MaxTenuringThreshold  ——设置升到老年代的最大年龄(默认15,新生区15次gc)

    例如: 

    -XX:+PrintGCDetails

    -XX:MaxTenuringThreshold=15   ——设置元空间大小为128m

    -XX:MetaspaceSize=128m   ——设置元空间大小为128m

    -XX:InitialHeapSize  ——初始化堆内存(默认本机内存的64分之1)

    -XX:MaxHeapSize  ——最大堆内存(默认本机内存的4分之1)

    -XX:ThreadStackSize ——单个线程栈的大小,查出来单位是k,其他都是b(windows根据本机内存大小一般默认为512k~1024k,linux64为1024k)

    利用jps -l  和 jinfo -flag 组合查看某进程的某参数是多少,例如查看元空间大小

    在VmOptions设置:-XX:MetaspaceSize=128m

    再次查看,元空间大小变成了128m

    jinfo -flags pid

  • 相关阅读:
    总有一天你将破蛹而出
    java 连接 Access数据库的两种方法
    freemarker中页面直接可以使用的内置对象
    freemarker中页面直接可以使用的内置对象
    常见的样式
    ibatis常用的集中判断语句
    mysql类型转换
    ibatis常用的集中判断语句
    window.open打开窗口时父窗口变成object
    window.open打开窗口时父窗口变成object
  • 原文地址:https://www.cnblogs.com/dream2true/p/10828769.html
Copyright © 2011-2022 走看看