一、虚拟机的发展
HotSpot VM(SUN) 以前使用范围最广的Java虚拟机
JRockit VM(BEA) 号称”世界上最快的Java虚拟机”
J9 VM(IBM)
Dalvik VM( Google )
HotSpot VM(ORACLE) 目前使用范围最广的Java虚拟机
二、JVM的整体介绍
三、运行时数据区
四、程序计数器
五、栈
栈(Stack):数据结构 入口和出口只有一个 入栈 出栈
特点 先进后出(FIL0)
异常:
线程请求的栈深度大于虚拟机所允许的深度:StackOverflowError
JVM动态扩展时无法申请到足够的内存时:OutOfMemoryError
六、虚拟机栈
七、本地方法栈
八、线程共享区域
方法区(永久代(JDK1.7和以前)、元空间(JDK1.8)) 类信息 常量 静态变量 即时编译期编译后的代码
Java堆(-Xms;-Xmx;-Xmn) 堆是需要重点关注的一块区域,因为涉及到内存的分配(new关键字,反射等)与回收(回收算法,收集器等) 几乎所有的对象都是在堆中分配.
九、JVM各版本内存区域的变化
运行时常量池 Class 文件中的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入这个区域。
JDK1.6 运行时常量池在方法区中
JDK1.7 运行时常量池在堆中
JDK1.8 去永久代:使用元空间(空间大小只受制于机器的内存)替代永久代
永久代参数
-XX:PermSize;-XX:MaxPermSize =100M 超过100M OOM()
元空间参数
-XX:MetaspaceSize; -XX:MaxMetaspaceSize
为什么会这样呢?
永久代来存储类信息、常量、静态变量等数据不是个好主意, 很容易遇到内存溢出的问题。 对永久代进行调优是很困难的,同时将元空间与堆的垃圾回收进行了隔离,避免永久代引发的Full GC和OOM等问题;
十、直接内存
不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域;
如果使用了NIO,这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作;
这块内存不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常;
避免了在Java 堆和Native 堆中来回复制数据,能够提高效率
十一、站在线程角度看JVM
十二、深入分析堆和栈
功能
以栈帧的方式存储方法调用的过程,并存储方法调用过程中基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放;(stackoverflow,OOM)
而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中;
线程独享还是共享
栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。
堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
空间大小
栈的内存要远远小于堆内存,栈的深度是有限制的,可能发生StackOverFlowError问题。
十三、JVM中对象的分配
十四、对象的内存布局
十五、对象的访问方式
句柄是一种特殊的智能指针 。句柄与普通指针的区别在于,指针包含的是引用对象的内存地址,而句柄则是由系统所管理的引用标识,该标识可以被系统重新定位到一个内存地址上。
十六、堆内存分配策略
堆进一步划分 新生代(PSYoungGen) Eden空间 From Survivor空间 To Survivor空间 老年代(ParOldGen)
堆中参数配置:
新生代大小: -Xmn20m 表示新生代大小20m(初始和最大)
-XX:SurvivorRatio=8 表示Eden和Survivor的比值, 缺省为8 表示 Eden:From:To= 8:1:1 2 Eden:From:To= 2:1:1
对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保
后续会持续更新JVM算法!!!