【JVM】第二章 Java内存区域
1、java内存管理的优势
相对于C语言需要开发人员自己手动的管理内存来说,java语言的自动内存回收机制是一大优势。但同时如果生产环境出现了内存溢出和泄露,就需要我们了解jvm的原理,以便更好的应用jvm提高性能和安全。
2、运行时数据区域(这是一个理论模型)
1)在说每个部分的作用之前说一下线程共享数据区和线程隔离数据区。
一个是共享就是这块区域是大家都可以用,一起分享的。
一个是线程隔离,说明它是基于线程存在的,每个线程都相互独立各不干扰。
- 线程隔离数据区
2)程序计数器
当前线程所执行的字节码的行号指示器。
jvm的多线程是通过线程轮流切换并分配处理器执行时间,为了线程切换后能恢复到上一次所执行的位置。所以每条线程都有一个独立的程序计数器。
3)虚拟机栈
就是我们所说的栈空间。每个线程由栈桢组成。每个栈桢由四部分组成:
局部变量表:方法的参数和方法内部的局部变量内存分配就在这里。
操作数栈:是一种stack结构。后进先出,通过压栈和出栈配合变量表进行相应的运算。
动态连接:这是针对java的特性设计的,只有只有在运行时才能知道它的直接引用地址在哪里。
返回地址:当方法正常或异常退出的时候,返回方法被调用的位置。
4)本地方法栈
与虚拟机栈发挥的作用相似,区别就是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则为虚拟机使用Native 方法服务。
- 线程共享数据区
5)堆
java虚似机管理的内存中最大一块。
所有线程共享的一块内存区域,在虚拟机启动时创建
此内存区域唯一目的就是存放对象实例。
所有的对象实例以及数组都要在堆上分配
Young区:S0+S1 、Eden (新生代)
Old区 (老年代)
6)方法区(非堆区、永久代)
所有线程共享
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据
CCS压缩指针:针对64位操作系统想使用32位的压缩地址表示。默认是开启的。
CodeCache: 存放 JIT编译后的代码。(特殊的操作指令集)
7)运行时常量池
方法区的一部分。class文件中除了类的版本、字段、方法、接口等信息外,还有一项就是常量池。用于存放编译期生成的各种字面量和符号引用。
3、对象创建(普通java对象)
1)当虚拟机机遇到一条new的指令时,首先它会去常量池中能不能定位到这个类的符号引用,并检查这个类是否被加载过。(见类加载过程分析)
2)检查通过之后,在Eden 区分配内存。如果是大对象可能分配在Old区。有参数可以配置,一般我们不用管
3)内存分配后,将内存空间都初始化为零
4)设置对象头。(哪个类的实例,类的元数据信息、对象hash、对象GC分代年龄等信息)
5)执行<init>方法进行初始化。
4、对象的访问
通过直接指针访问对象
这是一个操作数栈与堆之间对象访问图。可以看出在操作数栈本地变量表中一个reference类型它指向的一个分配在java堆空间的对象地址。
对象实例数据:对象头、实例数据(全局变量)、对齐填充(不够8个字段的进行填充)
对象类型数据:类信息、常量、静态变量、jit编译的代码