JVM笔记1

JVM内存与垃圾回收

jvm特点:内存动态分配和垃圾自动收集技术

jvmDOC网址:https://docs.oracle.com/javase/specs/index.html

 jvm书籍:

跨语言平台JVM:

Kotlin、Clojure、Groovy、Scala、Jython、JRuby、JavaScript可以编译为JVM可识别的字节码文件,运行在JVM平台上

JVM属于程序虚拟机,常见的虚拟机是系统虚拟机

 JVM整体结构

JVM的指令集架构:是基于栈的指令集架构(指令集架构还包括基于寄存器的),是零地址指令,因为基于栈可以跨平台,但是性能差。

JVM生命周期: 启动、执行、退出

常见java虚拟机:Classic VM(只有解释器)、Exact VM、Hotspot虚拟机(有方法区(永久代/元空间))、JRockit(服务端,只有JIT)、J9虚拟机   (后为三大商用虚拟机) apache harmony(android SDK)、Microsoft JVM、Taobao JVM、Dalvik VM(Android)、ART VM、Graal VM(可能替代Hotspot)

类加载子系统:

Loading: 获取二进制字节流-->方法区运行时数据结构-->内存中生成大的Class对象(作为类的数据的访问入口)

Linking:

Verify: 保证class文件符合JVM要求,主要做文件格式验证、元数据验证、字节码验证、符号引用验证

Prepare: 为类变量分配内存、赋初始值

Resolve: 将常量池的符号引用转换为直接引用

Initialization: 执行类构造器方法clinit,完成类变量和静态代码块中变量的赋值,该方法在多线程下是同步加锁的

类加载器:

分为引导类加载器(BootStrap ClassLoader)和自定义加载器(User-Defined ClassLoader)

ClassLoader派生的加载器都是自定义加载器

Extention ClassLoader(扩展类加载器)和Application ClassLoader(应用/系统类加载器)就是自定义加载器

AppClassLoader(系统类加载器)的上层是ExtClassLoader,ExtClassLoader的上层是BootStrap ClassLoader(用C/C++编写,获取不到,为null)

用户自定义类的加载器是AppClassLoader

Java的核心类库(包名为java、javax、sun)由BootStrap ClassLoader加载

双亲委派机制:收到类加载请求,优先由父类加载器加载,双亲委派机制可以做到沙箱安全机制

运行时数据区

PC寄存器(Program Counter Register)

用来存储指向下一条指令的地址,线程私有,既没有GC,也没有OOM。作用:线程切换后,记录下一指令开始位置

虚拟机栈(JVM Stacks):

线程私有,由一个一个栈帧组成,一个栈帧就是一个方法

设置栈的大小:  -Xxs1m

栈帧结构:局部变量表(Local Variables)、操作数栈(Operand Stack)、帧数据区:【动态链接(Dynamic Linking)、方法返回地址(Return Address)、附加信息】

局部变量表:基本单位是槽,32位(byte、short、char、boolean(0为false,非0为true)、引用类型 会被转换为int存储)占一个槽,64(long, double)位占两个槽;非静态方法的局部变量会多一个变量(this)

操作数栈:使用数组或链表实现,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时存储空间;栈顶缓存技术:频繁的执行内存读/写会影响执行速度,所以将栈顶元素全部缓存在物理CPU的寄存器中。

动态链接:指向运行时常量池的方法引用,将符号引用转换为直接引用

方法返回地址:存放该方法pc寄存器的值

附加信息:栈帧中还允许携带与虚拟机实现相关的附加信息(可选),例如对程序调试提供支持的信息

本地方法栈:

线程私有,用于管理本地方法的调用

本地方法: java调用非java接口(C/C++)

堆:进程(JVM实例)唯一,堆内存大小可调节,堆可以是物理不连续,逻辑上连续的;几乎所有的对象实例以及数组都应分配在堆上,是GC(Garbage Collection)回收的重点区域

堆空间逻辑上分为新生区/Young/New(Eden、s0、s1)+养老区/Old/Tenured,永久区jdk8之前/元空间jdk8物理上属方法区

参数设置:不写单位就是字节(bytes)

-Xms(memory start) :初始化大小,默认物理内存的1/64,

-Xmx :最大,默认物理内存的1/4

-XX:+PrintGCDetails:打印GC明细

-XX:NewRatio=2:表示新生代占1,老年代占2,默认为2

-XX:SurvivorRatio=8:Eden、S0、S1的比例8:1:1,默认是8,但是实际是自适应的,若要8需显示设置

-Xmn:设置新生代的大小,与-XX:NewRatio冲突,以-Xmn为准,不常用

-XX:MaxTenuringThreshold=:设置进入老年代的阈值

-XX:+PrintFlagsInitial:查看所有参数默认值

-XX:+PrintFlagsFinal:查看所有参数的最终值

-XX:HandlePromotionFailure:是否设置空间担保,为true时确认是进行Major GC还是Full GC,为false直接Full GC

-XX:+DoEscapeAnalysis开启逃逸分析

-XX:+PrintEscapeAnalysis查看逃逸分析结果

-XX:+EliminateAllocation标量替换

命令窗口查看参数命令:jps  

jstat  -gc 进程ID 查看内存占用

jinfo -flag SurvivorRatio 进程ID 查看参数值

Runtime .totalMemory()获取的是堆内存,此内存只包含一个s区,

OOM:OutOfMemoryError

年轻代(YoungGen):存放生命周期短的对象,又分为伊甸园(Eden)、幸存者0(Survivor0)、幸存者1(Survivor1)/(from区、to区(谁空谁是to)),80%都是朝生夕死

老年代(OldGen):存放生命周期长的对象,阈值为15,超过15去老年代

垃圾回收总结:频繁收集新生代,很少收集养老区,几乎不动永久代/元空间

Minor GC/Young GC、 Major GC/Old GC、 Full GC:Full GC收集java堆和方法区的垃圾收集;G1还有混合收集(Mixed GC)收集整个新生代以及部分老年代垃圾;Eden区满触发Minor GC,回收速度快;老年代空间不足触发Major GC,回收速度慢,比Minor GC慢10倍;System.gc()/老年代空间不足/方法区空间不足触发Full GC

常用调优工具:JDK自带、Eclipse的Memory Analyzer Tool、Jconsole、VisualVM、Jprofiler、Java Flight Recorder、GCViewer、GCEasy

堆内存分代的原因就是优化GC性能

TLAB( Thread Local Allocation Buffer):对Eden区划分,为每个线程分配一个私有缓存区域,优先使用TLAB,占Eden区的1%,

逃逸分析(只有server端才可用):如果一个对象没有逃逸出方法,就可能被优化成栈上分配,JDK6默认开启逃逸分析,尽量用局部变量,由JIT编译器优化;如果一个对象只有一个线程使用,省略同步考虑,同步省略;把没有逃逸的对象(聚合变量,不需要连续的内存结构也可以被访问)替换为标量,标量替换,可以存在寄存器(JVM栈)中;实际上对象是分配在堆上的

方法区(Hotspot中: 永久代/元空间)

线程共享,逻辑上是堆的一部分,物理上是独立于Java堆的内存空间,也叫Non-Heap

永久代使用的是虚拟机内存,JDK8废弃永久代,在本地内存中实现元空间

参数设置:

-XX:PermSize= 永久代初始分配空间大小,默认20.75M

-XX:MaxPermSize= 永久代最大分配空间,32位机器默认64M,64位机器默认82M

-XX:MetaspaceSize= 元空间分配内存大小,windows下是21M,超过该值就会触发Full GC,然后重置该值,使其不超过MaxMetaspaceSize

-XX:MaxMetaspaceSize= 元空间最大分配空间,windows下默认-1,没有限制

方法区内部结构:类信息(class、interface、enum、annotation、Field、Method)、运行时常量池、静态变量、即时编译器编译后的代码缓存

一个有效的字节码文件中包含类的版本信息、字段、方法、接口、常量池表(Constant Pool Table),常量池包括字面量和对类型、域和方法的引用

JDK1.7及以后将字符串常量池、静态变量保存到堆中,JDK1.8及以后的元空间使用的本地内存

对象实例: 由对象头、实例数据、对齐填充组成

对象头(Header): 运行时元数据(哈希值、GC分代年龄、锁状态)和类型指针(指向对象所属类型)

对象引用访问堆中对象:有句柄访问和直接指针两种方式;句柄访问:句柄池由到对象指针和到类型指针;直接指针(Hotspot用):直接指向对象,对象中有类型指针;后者效率高

直接内存:本地内存;直接分配本地内存空间:ByteBuffer.allocateDirect(1024*1024*1024),

访问直接内存的速度优于Java堆,读写性能高NIO,不受JVM内存回收管理,也会出现OOM,设置参数MaxDirectMemorySize,默认与堆内存-Xmx一样

执行引擎

将字节码指令编译为对应平台的本地机器指令

猜你喜欢

转载自blog.csdn.net/guoguolifang/article/details/113756987