java内存分析
先来看一下Java程序具体执行的过程:
运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。
1)堆(heap——线程共享):实例域,静态域,数组元素。
(1)新生代(young generation): 新创建对象的存放区域
a)伊甸区(eden):
b)幸存者0(survivor0):
c)幸存者1(survivor1):
(2)老年代(tenured | old generation):
2)虚拟机栈(stack——线程不共享):局部变量,方法自定义参数,异常参数。线程私有
3)方法区(permanent generation):保存类常量,字符串常量,加载类的素有class
4)、PC程序计数器
在JVM规范中规定,如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined。
由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。
----------------------------------------------------------------------------------------------------------------------------------------------------------
从上面图可以看出,堆和方法区是线程共享
栈、本地方法栈、程序计数器是线程私有
多线程共享部分:
- 共享堆
目的是存放对象实例,由所有线程共享,是内存中最大的一块,几乎所有的对象都在堆中分配实例内存。在堆的存储时,不需要连续存储空间,只需要逻辑连续就行。当堆的空间被用完时,会抛出OutOfMemoryError。
- 方法区
存储已经被虚拟机加载的类的信息、常量、静态变量、编译后的代码、class文件等。方法区也叫非堆(Non-Heap),在Sun HotSpot虚拟机中,将这块叫Permanent Generation永久区。
单线程私有部分:
- 程序计数器
每个线程的一块很小内存空间,用于存储自己的代码执行到哪个地方。在线程各个状态切换过程中能够保留程序执行状态。
- JVM栈
线程内部虚拟机栈,在线程创建时创建,用于存放局部变量、操作数栈、方法出口信息等。
- 本地方法栈
于JVM栈类似,区别是JVM栈是执行java服务,本地方法栈时为虚拟机调用操作系统本地方法服务的。有的虚拟机如Sun HotSpot直接将二者结合
------------------------------------------------------------------------------------------
GC垃圾回收器
Permanent Generation(永久代)是方法区,一般这块不参与GC。GC主要是回收Young Generation和 Old Generation部分。
---------------------------------------------------------------------------------------------------------------------
示例分析:
- String s1 = "china";
- String s2 = "china";
- String s3 = "china";
- String ss1 = new String("china");
- String ss2 = new String("china");
- String ss3 = new String("china");
这里解释一下黄色这3个箭头,对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。
量和引用存储在栈中,常量存储在常量池中
int i1 = 9;
int i2 = 9;
int i3 = 9;
public static final int INT1 = 9;
public static final int INT2 = 9;
public static final int INT3 = 9;
对于成员变量和局部变量:
成员变量就是方法外部,类的内部定义的变量;
局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。
形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
-----------------------------------------------------------------------------------------------------------------------
如以下代码:
Java代码
class BirthDate {
private int day;
private int month;
private int year;
public BirthDate(int d, int m, int y) {
day = d;
month = m;
year = y;
}
省略get,set方法………
}
public class Test{
public static void main(String args[]){
int date = 9;
Test test = new Test();
test.change(date);
BirthDate d1= new BirthDate(7,7,1970);
}
public void change1(int i){
i = 1234;
}
}
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:
1. main方法开始执行:int date = 9;
date局部变量,基础类型,引用和值都存在栈中。
2. Test test = new Test();
test为对象引用,存在栈中,对象(new Test())存在堆中。
3. test.change(date);
i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。
4. BirthDate d1= new BirthDate(7,7,1970);
d1为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。
5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收
-------------------------------------------------------------------------------------------------------------------------------------------------
示例2
package com.pb.neicun;
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Student student=new Student();
System.out.println("学生名字是:"+student.getName()+";"+"学生年龄是:"+student.getAge());
student.setName("候瑞阳");
student.setAge(28);
System.out.println("学生名字是:"+student.getName()+";"+"学生年龄是:"+student.getAge());
}
}
1、当我们运行程序时,JVM会把“Student”类与“Test”类编译完然后加载到JVM中一个叫方法区的地方,类的成员变量与成员方法也被加载到方法区中,此时内存模型如下。
2、当我们运行程序时,JVM会把“Student”类与“Test”类编译完然后加载到JVM中一个叫方法区的地方,类的成员变量与成员方 法也被加载到方法区中,此时内存模型如下
3、一个程序都是从主方法中开始执行。下面我们就开始从方法中第一个开始分析。
首先是“Student student = new Student();”
这里会在“栈”空间分配一个内存给我们对象“student”
然后在“堆”空间创建我们new出来的对象“Student()”
Student 所拥有的属性也在堆空间中,由“栈”空间创建的“student”对象找到“堆”创建的“student”,它找到的方法是通过 地址找到的。也就是“152aad01”(这个地址是本人编写的,目的是为了让大家更好的了解)。
4、再往下是“student.setName("侯瑞阳");”这里把“侯瑞阳”就赋值给name属性,那么name中的“null”改变为“侯瑞阳”。student.setAge(20);“age”由原来的“0”改变为“20”。
-----------------------------------------------------------------------------------------------------------------------------------------------------