在JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib合起来就称为jre。
JVM结构组成
运行时数据区
JVM在执行Java程序的过程中,会将它管理的内存划分为若干个不同的数据区域。
其中,虚拟机栈、本地方法栈、程序计数器这三个模块是线程私有的,方法区和Java堆是线程共享的
程序计数器(不会OOM)
线程私有的一块较小的内存,可以看作当前线程所执行的字节码的行号指示器。
每条线程都需要一个独立的程序计数器,以便线程切换后恢复到正确的执行位置。
虚拟机栈
线程私有的描述Java方法执行的线程内存模型
每个方法被执行时,虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
栈运行原理:
栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,B方法又调用了C方法,于是产生栈帧F3也被压入栈…… 依次执行完毕后,先弹出后进......F3栈帧,再弹出F2栈帧,再弹出F1栈帧
JAVA虚拟机栈的最小单位可以理解为一个个栈帧,一个方法对应一个栈帧,一个栈帧可以执行很多指令
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存时就会抛出OutOfMemoryError异常
本地方法栈
与虚拟机栈类似,为虚拟机用到的本地方法服务
在栈深度溢出或扩展失败时抛出StackOverflowError和OutOfMemoryError异常
Java堆
虚拟机所管理的内存中最大的一块,堆是被所有线程共享的一块内存,在虚拟机启动时创建,为存放对象实例。
Java堆中没有内存完成实例分配,并且也无法扩展时,会抛出OutOfMemoryError异常
方法区
各个线程共享的一块内存区域,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据
被装载的class的信息存储在Methodarea的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中
此外,运行时常量也是方法区的一部分。
如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常
运行时常量池
方法区的一部分,用于存放编译期生成的各种字面量与符号引用
直接内存
Native函数库直接分配的堆外内存,通过存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作,避免在Java堆和Native堆中来回复制数据
内存分配策略
1. 对象优先在 Eden 分配
大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。
2. 大对象直接进入老年代
大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。
经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
-XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 和 Survivor 之间的大量内存复制。
3. 长期存活的对象进入老年代
为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。
-XX:MaxTenuringThreshold 用来定义年龄的阈值。
4. 动态对象年龄判定
虚拟机并不是永远要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。
5. 空间分配担保
在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。
如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。
java代码编译(Java Compiler)过程
也就是由.java文件到.class文件的过程
(源词双语字节符,token流向语法树)