java 内存编译_jvm内存模型,java类从编译到加载到执行的过程,jvm内存分配过程...

一、jvm内存模型

JVM 内存模型主要分为堆、程序计数器、方法区、虚拟机栈和本地方法栈

cfa28227780133ad23b86dc57cef78cc.png

1、堆

1.1、堆是 JVM 内存中最大的一块内存空间。

1.2、该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。

1.3、堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,最后 Survivor 由 From Survivor 和 To Survivor 组成。

2、程序计数器(Program Counter Register)

程序计数器是一块很小的内存空间,用来记录下一条运行的指令(实际是记录各个线程执行的字节码的地址,由于 Java 是多线程语言,当执行的线程数量超过 CPU 核数时,线程之间会根据时间片轮询争夺 CPU 资源。如果一个线程的时间片用完了,或者是其它原因导致这个线程的 CPU 资源被提前抢夺,那么这个退出的线程就需要单独的一个程序计数器,来记录下一条运行的指令。),例如,分支、循环、跳转、异常、线程恢复等都依赖于计数器。

3、方法区(Method Area)

3.1、方法区!=永久代,根据虚拟机类型而定;HotSpot 虚拟机使用永久代来实现方法区,但在其它虚拟机中,例如,Oracle 的 JRockit、IBM 的 J9 就不存在永久代一说。

3.2、方法区主要是用来存放已被虚拟机加载的类相关信息,包括类信息(类的版本、字段、方法、接口和父类等信息)、运行时常量池、字符串常量。

ps1:JVM 在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。在加载类的时候,JVM 会先加载 class 文件,而在 class 文件中除了有类的版本、字段、方法和接口等描述信息外,还有一项信息是常量池 (Constant Pool Table),用于存放编译期间生成的各种字面量和符号引用()。

ps2:字面量包括字符串(String a=“b”)、基本类型的常量(final 修饰的变量)。

ps3:符号引用则包括类和方法的全限定名(例如 String 这个类,它的全限定名就是 Java/lang/String)、变量的名称和描述符以及方法的名称和描述符。

ps4:当类加载到内存中后,JVM 就会将 class 文件常量池中的内容存放到运行时的常量池中。

ps5:在解析阶段,JVM 会把符号引用替换为直接引用(对象的索引值)。

3.3、方法区与堆空间类似,也是一个共享内存区,所以方法区是线程共享的。

3.2、在 Java6 版本中,永久代在非堆内存区;到了 Java7 版本,永久代的静态变量和运行时常量池被合并到了堆中;而到了 Java8,永久代被元空间取代了,并且元空间的存储位置是本地内存,永久代的静态变量(class static variables)以及运行时常量池(runtime constant pool)则跟 Java7 一样,转移到了堆中。如下图:

4ab56cfa2fac9edb928d504d62e6d5cd.png

ps1:java8用直接内存元空间替代永久代的好处

1-1、移除永久代是为了融合 HotSpot JVM 与 JRockit VM 而做出的努力,因为 JRockit 没有永久代,所以不需要配置永久代。

1-2、永久代内存经常不够用或发生内存溢出,爆出异常 java.lang.OutOfMemoryError: PermGen。这是因为在 JDK1.7 版本中,指定的 PermGen 区大小为 8M,由于 PermGen 中类的元数据信息在每次 FullGC 的时候都可能被收集,回收率都偏低,成绩很难令人满意;还有,为 PermGen 分配多大的空间很难确定,PermSize 的大小依赖于很多因素,比如,JVM 加载的 class 总数、常量池的大小和方法的大小等。

4、虚拟机栈(VM stack)

4.1、Java 虚拟机栈是线程私有的内存空间,它和 Java 线程一起创建。

4.2、用来保存方法的局部变量、操作数栈、动态链接方法和返回地址等信息,并参与方法的调用和返回。每一个方法的调用都伴随着栈帧的入栈操作,方法的返回则是栈帧的出栈操作。

5、本地方法栈(Native Method Stack)

5.1、本地方法栈跟 Java 虚拟机栈的功能类似,Java 虚拟机栈用于管理 Java 函数的调用,而本地方法栈则用于管理本地方法的调用。

5.2、本地方法并不是用 Java 实现的,而是由 C 语言实现的。

二、java类从编译到加载到执行的过程

f177bd91a37c1c71bbffc9bb47afa3e4.png

1、编译

通过jdk自带的javac工具完成

2、类加载

2.1、当一个类被创建实例或者被其它对象引用时,虚拟机在没有加载过该类的情况下,会通过类加载器将字节码文件加载到内存中。

2.2、不同的实现类由不同的类加载器加载,JDK 中的本地方法类一般由根加载器(Bootstrp loader)加载进来,JDK 中内部实现的扩展类一般由扩展加载器(ExtClassLoader )实现加载,而程序中的类文件则由系统加载器(AppClassLoader )实现加载。

2.3、在类加载后,class 类文件中的常量池信息以及其它数据会被保存到 JVM 内存的方法区中。

3、类连接

类在加载进来之后,会进行连接、初始化,最后才会被使用。在连接过程中,又包括验证、准备和解析三个部分。

3.1、验证

验证类符合 Java 规范和 JVM 规范,在保证符合规范的前提下,避免危害虚拟机安全。

3.2、准备

为类的静态变量分配内存,初始化为系统的初始值。对于 final static 修饰的变量,直接赋值为用户的定义值。例如,private final static int value=123,会在准备阶段分配内存,并初始化值为 123,而如果是 private static int value=123,这个阶段 value 的值仍然为 0。

3.3、解析

将符号引用转为直接引用的过程。我们知道,在编译时,Java 类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。类结构文件的常量池中存储了符号引用,包括类和接口的全限定名、类引用、方法引用以及成员变量引用等。如果要使用这些类和方法,就需要把它们转化为 JVM 可以直接获取的内存地址或指针,即直接引用。

3.4、类初始化

编译器会在将 .java 文件编译成 .class 文件时,收集所有类初始化代码,包括静态变量赋值语句、静态代码块、静态方法,收集在一起成为 () 方法。在这个阶段中,JVM 首先将执行构造器 方法

初始化类的静态变量和静态代码块为用户自定义的值,初始化的顺序和 Java 源码从上到下的顺序一致。例如:

private static int i=1;

static{

i=0;

}

public static void main(String [] args){

System.out.println(i);

}

运行结果为:0

子类初始化时会首先调用父类的 () 方法,再执行子类的 () 方法,运行以下代码:public classParent{

public static String parentStr= "parent static string";static{

System.out.println("parent static fields");

System.out.println(parentStr);

}publicParent(){

System.out.println("parent instance initialization");

}

}public class Sub extendsParent{public static String subStr= "sub static string";static{

System.out.println("sub static fields");

System.out.println(subStr);

}publicSub(){

System.out.println("sub instance initialization");

}public static voidmain(String[] args){

System.out.println("sub main");newSub();

}

}

运行结果为:

parent static fields

parent static string

sub static fields

sub static string

sub main

parent instance initialization

sub instance initialization

JVM 会保证 () 方法的线程安全,保证同一时间只有一个线程执行。

JVM 在初始化执行代码时,如果实例化一个新对象,会调用 方法对实例变量进行初始化,并执行对应的构造方法内的代码。

类初始化完后就可以正式使用了,比如创建对象等。

4、运行时编译

三、jvm内存分配过程

1、JVM 根据配置配置或默认参数向操作系统申请内存空间

2、JVM 获得内存空间后,会根据配置参数分配堆、栈以及方法区的内存大小

3、class 文件加载、验证、准备以及解析,其中准备阶段会为类的静态变量分配内存,初始化为系统的初始值

4、使用过程中内存分配

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值