1.jdk的体系结构
jdk
开发工具包中包含java代码运行时所需要的jre
,在jre
中,又包含了jvm
。
- 接下来我们再来看看当下
java虚拟机
的主要阵营,其中比较常见的事sun公司的jvm
,首先java
代码经过javac
命令编译成jvm
所熟知的.class
文件,然后通过jvm
的类加载子系统
加载到jvm
中,java
代码具有一次编译,到处运行的特点(跨平台)。
2.java虚拟机的构成
2.1 java虚拟栈
Java虚拟栈
:这个也是一个线程私有的,生命周期与线程是同步的,每个方法在执行的同时,都会创建一个栈帧,用于存储局部变量表
,操作数栈
,动态链接
,方法出入口
等信息,每个方法的调用到执行完成的过程就是一个栈帧入栈到出栈的过程;如图:
- 在这里
动态链接
里面存放的是类的方法对应的jvm子密码的内存地址
,在Class文件中的常量池
中存有大量的符号引用。字节码中的方法调用指令就以常量池
中指向方法的符号引用作为参数。这些符号引用一部分在类的加载阶段或第一次使用
的时候就转化为了直接引用,这种转化称为静态链接
。而相反的,另一部分在运行期间
转化为直接引用,就称为动态链接
2.2 程序(线程)计数器
程序(线程)计数器
:是一块较小的内存空间,用来指定当前线程执行字节码
的行数,每个线程计数器都是私有
的,因为每个线程都需要记录执行的行数;这里解释一下为什么每个线程都需要一个线程计数器,JVM的多线程是通过线程轮流切换分配执行时间来实现的,在任何时刻,每个处理器都只会执行一个线程中的指令,当线程进行切换的时,为了线程能恢复当正确的位置
,所以每个线程必须有个独立的线程计数器,这样才能保证线程之间不互相影响
2.3 本地方法栈
本地方法栈
:与虚拟机栈执行的基本相同,唯一的区别就是虚拟机栈是执行Java方法的,本地方法栈是执行native
方法的, 值得注意的是本地方法是用C语言实现的,由于java刚起源时很多底层实现都是C语言写的,后来做java代码交互的时候也保留了一些C语言实现的方法,就是现在的native
本地方法
2.4 Java堆
Java堆
:堆区是Java虚拟机所管理的内存中最大的一块,Java堆是被所有线程共享
的内存区域,主要存储对象的实例,当堆中没有内存完成实例分配,并且堆无法扩展的时候,将会抛出OutOfMemoryError
异常;当前虚拟机都是可以扩展的
2.5 方法区
方法区
:这个也是线程共享
的内存区域,存储被虚拟机加载的类信息
、常量
、静态变量
、即时编译的代码数据
等,方法区使用的是java虚拟机之外的直接内存- 方法区在物理上也是不需要连续的,可以选择固定大小或者扩展的大小,还可以选择不实现垃圾收集,方法区的垃圾回收是比较少的,这就是方法区为什么被称为永久区的原因,但是方法区也是可以执行回收的,该区域主要是针对常量池和类型的卸载,在方法区也规定当方法区无法满足内存分布的时候,将会抛出
OutOfMemoryError
异常 运行时常量
是方法区的一部分,常量池主要用于存放编译生成的各种自变量和符合引用,由于常量池属于方法区的一部分,所以当常量池没有内存空间的时候就抛出OutOfMemoryError
异常
2.6 直接内存
-
直接内存
:不是虚拟机运行时的一部分,可以直接访问堆外的内存,所以当内存空间无法动态扩展的时候就会出现OutOfMemoryError
异常 -
以上基本是JVM内存分布的内容,简单的理解水满则溢出就是这个道理,系统的整个空间是一个大的容器,分不同的部分或者桶去分担整个容量,当那个桶不够的时候自然会溢出,明白内存区域的分布我们看下对象是如何分配在内存空间里面的?
-
例
: Java对象这里指的是引用类型的对象,这里用Student stu=new Student()
为例子访问,stu
作为引用对象,存在与Java虚拟机栈上,new Student()
保存在Java堆
中,堆中记录Student类型的信息包括方法
,接口
,对象类型
等地址,这些类型的执行的数据存储在方法区中
3. 堆的结构
- 类 new出来的对象存放在堆内存中,堆被划分为新生代和老年代,新生代又被进一步划分为
Eden
和Survivor
区,而Survivor由FromSpace
和ToSpace
组成:,新生代占内存1/3,老年代占内存2/3: - 1、大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次
MinorGC
- 2、执行GC后,将存活的对象分配到
Survivor
空间 - 3、无法放到Survivor空间的对象,分配到老年代(默认对象执行15次 Minor GC之后就放入老年代)
- 4、分配到Survivor的对象,经过多次Minor GC后,进入老年代
新生代GC(Minor GC)
:指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快
老年代GC(Full GC)
:指发生在老年代的GC,出现了Full GC,经常会伴随至少一次的Minor GC,Full GC的速度一般会比Minor GC慢10倍以上。Eden区空间不足,触发MinorGC;老年代空间不足,触发Full GC(值得注意的是调用System.gc()
方法也可以触发Full Gc )。
总结
:关于java虚拟机的调优,减少Full GC
的触发次数,减少Full GC
的持续时间
4.各个模块总体关系图
- 最后总结一下各个模块的总体关系图,有兴趣的同学可以多多交流。