【Java的内存分配】:
1.Java程序运行时的内存结构分成:方法区,栈内存,堆内存,本地方法栈,寄存器区
【方法区】:
1.存放被装载类的数据信息,即存放该类的定义信息。类装载之后,类的定义(.class)字节码存放在方法区。具体信息包括:
基本信息:
1)每个类的全限定名
2)每个类的直接超类的权限定名(可约束类型转换)
3)该类是类还是接口(类型)
4)该类型的访问修饰符
5)直接超接口全限定名的有序列表
【说明】:
全限定名:带包结构的类名。
【栈内存】:
1.Java内存中以帧的形式存放本地方法的调用状态(包括方法调用的参数,局部变量,中间结果)。
也就是说:在方法中定义的一些基本类型的变量和对象的引用变量都在方法的栈内存中分配。
2.栈内存的构成:由局部变量区,操作数栈,帧数据区组成。
(1).局部变量区为一个以字为单位的数组,每个数组元素对应一个局部变量的值。调用方法时,
将方法的局部变量组成一个数组,通过索引来访问。若为非静态方法,则加入一个隐含的引用参数this,
该参数指向调用这个方法的对象。而静态方法则没有this参数。因此,对象无法调用静态方法。
(2).操作数栈也是一个数组,但是通过栈操作来访问。所谓操作数栈是那些被指令操作的数据。
当需要对参数操作时如a=b+c,就将即将被操作的参数压栈,如将b和c压栈,然后由操作指令将
他们弹出去,并执行操作。虚拟机将操作数栈作为工作区。
(3)帧数据区处理常量池解析,异常处理。
3.栈有一个很重要的特性,就是存在栈中的数据可以共享。而且栈中的数据是不能修改的,只有
进栈(push)和弹栈(pop)。
[说明]:
(1).栈内存存放的是:基本数据类型数据的值;变量的内存分配的值。
(2).帧的形式:指类似一格一格的形式,每一帧就是栈中的一个数据。
(3).调用状态:说明是运行起来的,在动的,不是静止的。
总之,操作数栈存放的是:基本数据类型的值,引用变量的内存 分配的地址。
【堆内存】:
1.堆内存用来存放由new创建出来的对象和数组。在堆中的分配的内存,由Java虚拟机的自动垃圾回收器来管理。
2.在堆中产生了一个数组或对象后,还可以在堆中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在
堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于是为数组或对象起的一个名称,
以后就可以在程序中使用栈中的引用变量来访问堆中数组或变量。
[说明]:
栈内存和堆内存的比较:
1.栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,
缺乏灵活性。栈中主要存放一些基本数据类型的变量(int,short,long,byte,float,double,boolean , char)
和对象句柄。
2.堆内存:存放的是new创造的内存空间,即new的东西都是寸方法在堆中。
3.栈内存中的数据,Java虚拟机不负责回收垃圾。用因为栈中一般存放的是临时变量,用完了(出了作用域)之后会自己维护清理弹出栈的。
方法区是不存在内存清理的,因为一个类中的方法等信息,以后可能还会用,不能清除, 只有在虚拟机关闭时才清理。
【本地方法内存栈】:
1.Java通过本地JNI(Java Native Interface)来调用其他语言编写的程序,用native修饰符来描述一个方法是本地方法。
栈内存、堆内存分析:
(1)栈内存分析:
int a = 3;
int b = 3;
a = 4;
如果一个变量被重新赋值,只是变量区中的变量指向操作数区的另一个操作数(如果该操作数在该区中已经存在,则直接指向,否则把该操作数添加到栈中)。
内存示意图分析:
【说明】:编译器先处理int a =3; 首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3.接着处理int b=3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3.这样,就出现了a与b同时指向3的情况。此时,a==b的结果为true。
这时,如果再令a=4;那么编译器会重新搜索栈中是否有4这个操作数,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b值,即a与b不捆绑。
(2)堆内存分析:
T t ;
t = new T();
T t2 = t;-----t,t2会捆绑吗?
t2 = new T();----t,t2会捆绑吗?
【说明】:t对象进栈
【说明】:t对象在堆内存中创建对象
【说明】:t2对象指向t的引用,t和t2捆绑
【说明】:t2在堆内存中创建对象,t,t2不会捆绑