一、JAVA内存管理与GC机制
Java在JVM所虚拟出的内存环境中运行,java内存分为栈(stack)和堆(heap)两部分。 栈
在Java中,JVM中的栈记录了线程的方法调用。每个线程拥有一个栈,线程创建时创建栈。在某个线程的运行过程中,如果有新的方法调用,那么该线程对应的栈就会增加一个存储单元,即帧(frame)。在frame中,保存有该方法调用的参数、局部变量、临时数据和返回地址。 栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中。 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。 当被调用方法运行结束时,该方法对应的帧将被删除,参数和局部变量所占据的空间也随之释放。线程回到原方法,继续执行。当所有的栈都清空时,程序也随之运行结束。
2.堆
Java的普通对象存活在堆中。与栈不同,堆的空间不会随着方法调用结束而清空。因此,在某个方法中创建的对象,可以在方法调用结束之后,继续存在于堆中。这带来的一个问题是,如果我们不断的创建新的对象,内存空间将最终消耗殆尽,JVM启动时创建堆。 存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令) jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
3.方法区
方法区是系统分配的一个内存逻辑区域,是用来存储类型信息的(类型信息可理解为类的描述信息) 又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
4.运行时数据区过程 AppMain.javapublic class AppMain //运行时, JVM把AppMain的信息都放入方法区{ public static void main(String[] args) //main 方法本身放入方法区。 { //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面 Sample test1 = new Sample( " 测试1 " ); test1.printName(); }}Sample.javapublic class Sample //运行时, jvm 把appmain的信息都放入方法区{ private name; //new Sample实例后,name引用放入**栈**区里name对象放入堆里 public Sample(String name) { this .name = name; } public void printName() //print方法本身放入方法区里。 { System.out.println(name); }}
统收到了我们发出的指令,启动了一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的方法区中。这一过程称为AppMain类的加载过程。
接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。
执行main()方法第一条指令:
Sample test1=new Sample(“测试1”);
JVM执行该任务过程: JVM在方法区查找Sample类信息,若没有Sample信息,则装载Sample类,然后把Sample类型信息放在方法区里。 创建实例:首先在堆区为新的实例分配内存,这个Sample实例持有者指向方法区Sample类型信息的引用。这里所说的引用,实际上指的是Sample类的类型信息在方法区中的内存地址。而这个地址呢,就存放了在Sample实例的数据区里。 位 于“=”前的test1是一个在main()方法中定义的变量,可见,它是一个局部变量,因此,它被会添加到了执行main()方法的主线程的JAVA方法调用栈中。而“=”将把这个test1变量指向堆区中的Sample实例,也就是说,它持有指向Sample实例的引用。
5.在Java语言里堆(heap)和栈(stack)里的区别 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第6点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
|