本文概述
Java虚拟机JVM总结
参考:
Understanding Java Memory Model
JVM
基本概念
JVM为Java虚拟机,包括一套字节码指令集,一组寄存器,一个栈,一个垃圾回收,堆和一个存储方法域。JVM运行在操作系统之上的,他与硬件没有直接的交互
- java代码的执行
- 代码编译为class -> javac
- 装载class -> ClassLoader
- 执行class
- 解释执行
- 编译执行: client compiler/ server compiler
- 内存管理
- 内存空间
- 方法区
- 方法栈
- 堆
- 本地方法栈
- pc寄存器
- 内存分配
- 堆上分配
- TLAB分配
- 栈上分配
- 内存回收
- 算法(Copy / Mark-Sweep / Mark-Compact)
- Sun JDK
- 分代回收
- 新生代可用的GC(串行copying,并行回收copying,并行copying)
- Minor GC触发机制以及日志格式
- 旧生代可用的GC(串行Mark-Sweep-Compact,并行Compacting,并发Mark-Sweep)
- Full GC触发机制以及日志格式
- GC参数
- G1
- 分代回收
- 内存状况分析(jconsole, visualvm,jstat,jmap,MAT)
- 内存空间
- 线程资源同步和交互机制
- 线程资源同步
- 线程资源执行机制
- 线程资源同步机制(Synchronized, lock/ unlock)
- 线程交互机制
- wait, notify, notifyAll, double check pattern
- 并发包提供的交互机制(semaphore,CountwnLatch)
- 线程状态及分析方法(jstack,TDA)
- 线程资源同步
运行过程
java源文件 –> 编译器 –> 字节码文件
字节码文件 –> JVM –> 机器码、
当一个程序从开始运行,这时虚拟机开始实例化,多个程序启动就存在多个虚拟机实例,程序退出或者关闭,则虚拟机实例小王,多个虚拟机实例之间数据不能共享。
JVM允许一个应用并发执行多个线程。Hotspot JVM中的Java线程与原生操作系统线程有直接的映射关系。当本地存储,缓冲区分配,同步对象,栈,程序计数器等准备好以后,就会创建一个操作系统原生线程。Java线程结束,原生线程随之被回收 。操作系统负责调度所有的线程,并把他们分配到任何可用的CPU上,当原生线程初始化完毕,就会调用Java线程的run()方法,当线程结束时,会释放原生线程和Java的所有资源。
JVM Memory Model
运行资源密集型Java程序时,您必须使用一下某些JVM内存配置
- XmsSetting – 初始堆大小
- XmxSetting – 最大堆大小
- XX: NewSizeSetting – 新一代堆大熊啊
- XX:MaxNewSizeSetting —最大新一代堆大小
- XX:MaxPermGenSetting —永久生成的最大大小
- XX:SurvivorRatioSetting-新的堆大小比率(例如,如果Young Gen大小为10m并且内存开关为–XX:SurvivorRatio = 2,则将为Eden空间保留5m,为两个Survivor空间分别保留2.5m,默认值= 8)
- XX:NewRatio —提供新旧大小的比率(默认值= 2)
堆内存
- 堆内存分为年轻一代和老一代
- JVM启动时分配堆
- 应用程序运行时堆大小增加或减少
- 最大大小: -Xms
年轻一代
- 保留用于包含新分配的对象
- Young Gen包括三个部分-Eden Memory 和两个Survivor Memory空间(S0,S1)
- 大多数新创建的对象进入Eden Memory
- 当Eden Memory 空间中充满对象时,将执行MinorGC(又称Young Collection),并将所有幸存对象移动到其中一个幸存者空间。
- Minor GC还检查survivor 对象并将其移至其他survivor 空间。因此,其中一个survivor空间经常是空的。
- 在许多次GC循环后,仍然存在的对象将移至旧代存储空间。通常,可以通过设置年轻一代对象的年龄阈值,然后再有资格晋升为老一代
老一代
- 这是为了包含可能在多次循环在GC中存货下来长寿命的对象而保留的
- 当旧一代空间已满时,将执行Major GC(又称Old Collection),需要的时间更长
非堆内存
- 包括永久生成
- Perm Gen存储每个类的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,内部字符串
- 可以使用-XX: PermSize和-XX: MaxPermSize更改其大小
缓存
- 代码缓存
- 存储由JIT编译器生成的已编译代码,JVM内部结构,已加载的探查气代理代码和数据等。
- 当代码缓存超过阈值时,他将被刷新,并且GC不会从新定位对象。
Stack VS Heap
Java Stack内存用于执行线程,它包含方法特定的值以及对Heap中其他对象的引用:
例:
1 | class Person { |
新版Java引入以下新的内存空间
- Keep Area- Young Generation中的新存储空间,用于包含最近分配的对象,直到下一代Young才执行GC。该区域可防止仅由于在开始young collection之前就已经分配对象
- Metaspace:Metaspace替代Permanent Generation。即使Perm Gen始终具有固定大小,它也可以自动增加其大小从而达到基础操作系统所提供的大小。只要classloader 处于活跃装填,Metadata保持活跃且无法释放。