类的加载和内存分配的过程
1.类的加载过程
在java应用程序开发中,只有被java虚拟机装载的Class类型才能在程序中使用。只要生成的字节码符合java虚拟机的指令集和文件格式,就可以在JVM上运行
-
class文件加载
- 将编译好的.java程序,通过java编译器生成.class文件,然后由类加载机制加载到jvm中,存储在方法区中。
- 并且会随之生成一个相应的Class对象,这个对象一般也是存放于方法区中的,他包含了有关类的信息(版本,字段,方法…),一般也会存放在方法区中。
-
装载
- 验证:验证字节码文件是否符合规范。
- 准备:对静态域分配内存并进行默认初始化。(方法区中进行分配)
- 解析:虚拟机常量池内的符号引用替换为直接引用的过程。(比如String s =“hello”,转化为 s的地址指向“aaa”的地址)
-
初始化
为静态域执行初始化,如果该类有父类,就先对其父类进行初始化。实际过程中如果没有对该字段进行引用则不用进行初始化。
2.jvm内存分配
jvm的内存主要分为三部分:堆、栈、方法区。其实每个部分还包括分区。
我们知道java是多线程的,其中堆和方法区是线程共享的。栈是线程私有数据区。
public class test{
static int i=1;
final q=5;
String s="hello";
float f=2.0f;
public void f(){
int a=10;
}
public static void ff(){}
public static void main(String args[]){
test t=new test();
t.f();
String ss=new String("hello");
}
}
拿上面的java程序举例:
-
首先test.class文件被类加载器加载到jvm,存放在方法区中,class文件中除了存放类的信息,字段、方法等描述信息之外,还有个常量池:包含了在编译期生成的字面量(量本身-也就是值)和符号引用(在解析过程变成直接引用)这部分的内容在类加载进入方法区的常量池中存放
由此我们可以知道反射的基础:
在装载类的时候,加入方法区中的所有信息,最后都会形成Class类的实例,代表这个被装载的类。方法区中的所有的信息,都是可以通过这个Class类对象反射得到。我们知道对象是类的实例,类是相同结构的对象的一种抽象。同类的各个对象之间,其实是拥有相同的结构(属性),拥有相同的功能(方法),各个对象的区别只在于属性值的不同。同样的,我们所有的类,其实都是Class类的实例,他们都拥有相同的结构-----Field数组、Method数组。而各个类中的属性都是Field属性的一个具体属性值,方法都是Method属性的一个具体属性值。
-
经过一系列过程(验证-准备-解析-初始化)找到程序入口main()方法
-
main()方法进栈,创建一个test引用变量,并为其分配空间。对引用变量进行初始化,指向的是堆内存中实例的地址。当对象在堆内存中创建出来后成员变量也随之被存储在对象中。
-
f()方法进栈,为局部变量a分配空间,进行初始化,f()出栈,释放局部变量a所占的栈空间,销毁。
-
创建字符串引用常量,分配内存空间,初始化值,指向堆内存中的对象地址,堆内存对象指的是常量池中的字符串常量hello的地址。
成员变量:定义再类中,作用于全类,存在于堆内存中的对象中的,随着对象的消亡而消亡,所以不管是基本类型还是引用类型都是在堆内存的对象中的。这里的s在堆内存中存放的是常量池中hello的地址值。
**局部变量:**定义在方法中,随着方法的进栈相应的会为该局部变量开辟栈空间,随着方法的出栈而销毁,此时的引用类型变量在栈空间存储的是对象的地址,所指向的对象是存放于堆内存中的。
常量:字符串常量和基本类型常量都是存放在方法区中的常量池的。
总结:
- 栈中的数据和堆中的数据销毁并不是同步的。方法一旦结束,栈中的局部变量立即销毁,但是堆中对象不一定销毁。因为可能有其他变量也指向了这个对象,直到栈中没有变量指向堆中的对象时,它才销毁,而且还不是马上销毁,要等垃圾回收扫描时才可以被销毁。
- 以上的栈、堆、代码段、数据段等等都是相对于应用程序而言的。每一个应用程序都对应唯一的一个JVM实例,每一个JVM实例都有自己的内存区域,互不影响。并且这些内存区域是所有线程共享的。这里提到的栈和堆都是整体上的概念,这些堆栈还可以细分。
- 类的成员变量在不同对象中各不相同,都有自己的存储空间(成员变量在堆中的对象中)。而类的方法却是该类的所有对象共享的,只有一套,对象使用方法的时候方法才被压入栈,方法不使用则不占用内存。
这3篇关于内存分配和类加载讲的很详细
https://www.cnblogs.com/langtianya/p/4441206.html