java内存分析
http://blog.csdn.net/lidaasky/article/details/7931560
java中,除了8种基本类型,其他都是引用类型。
局部变量:方法体内声明的变量,包括形参都是局部变量方法体外。
成员变量:类内声明的变量为成员变量。
由图可以看出,执行步骤:
1、从硬盘中提取代码到内存中。
2、代码存放在代码段;局部变量和基本数据类型存放于栈(Stack);引用类型存放于堆(Heap)中;静态变量、字符串存放于数据段。
内存分析
* 一维数组例如一维数组的初始化
[java]
1. int [] s;s=new int [5];
* 先是在栈中声明s:
在内存中是s指向了堆内的值:
如果数组的项是引用类型,如下
1. public class Test{
2. public static void main(String args[]){
3. Date []days;
4. days=new Date[3];
5. for (int i=0;i<3;i++){
6. days[i]=new Date(2004,4,i+1);
7. }
8. }
9. }
10. class Date{
11. int year;int month;int day;
12. Date(int y;int m;int d){
13. year=y;
14. month=m;
15. day=d;
16. }
17. }
执行Date [] days;在栈中给days分配空间:
执行days= new Date[3];在堆中给days分配三个位置,内容为空,days指向这三个位置:
执行循环后,每个days项再指向堆中分配的时间位置:
二维数组二维数组可以看成以数组为元素的数组。例如:
int a[][]={{1,2},{3,4,5,6},{7,8,9}};
内存回收
栈里面的内存,函数执行完后消失,而堆里面的内存,需要垃圾回收机制回收,不一定马上消失。
实参与局部变量同等对待;函数返回值也会在栈里面临时存放,函数就执行完也就会删除,调用完毕后内存回收,但是只分配空间,没有名字。
构造函数
1. Public class Person{
2. int id;
3. int age=20;
4. Person(int _id,int _age){
5. id=_id;
6. age=_age;
7. }
8. Pubic static void main(String [] args){
9. Person tom=new Person(1,25);
10. }
11. }
把栈中的值赋给堆中new出来的tom对象:
执行完毕后,方法中的局部变量在栈中所占空间被收回。
我们都知道this指向的是对象本身,以下这张图可以形象的看出,this是如何指向自身:
static变量静态变量,分配空间时,位于的是数据段,而并非堆和栈中。
-
static变量
静态变量,分配空间时,位于的是数据段,而并非堆和栈中。
super关键字
1. class FatherClass {
2. public int value;
3. public void f(){
4. value = 100;
5. System.out.println
6. ("FatherClass.value="+value);
7. }
8. }
9. class ChildClass extends FatherClass {
10. public int value;
11. public void f() {
12. super.f();
13. value = 200;
14. System.out.println
15. ("ChildClass.value="+value);
16. System.out.println(value);
17. System.out.println(super.value);
18. }
19. }
20. public class TestInherit {
21. public static void main(String[] args) {
22. ChildClass cc = new ChildClass();
23. cc.f();
24. }
25. }
super指向的是该对象的类继承的类的对象,如下
继承
1. class Person {
2. private String name;
3. private int age;
4. public void setName(String name) {
5. this.name=name;
6. }
7. public void setAge(int age) {
8. this.age=age;
9. }
10. public String getName(){
11. return name;
12. }
13. public int getAge(){
14. return age;
15. }
16. }
17. class Student extends Person {
18. private String school;
19. public String getSchool() {
20. return school;
21. }
22. public void setSchool(String school) {
23. this.school =school;
24. }
25. }
26. public class Test {
27. public static void main(String arg[]){
28. Student student = new Student();
29. student.setName("John");
30. student.setAge(18);
31. student.setSchool("SCH");
32. System.out.println(student.getName());
33. System.out.println(student.getAge());
34. System.out.println(student.getSchool());
35. }
36. }
由图可以看出,继承的类实例化时,其内部包含其父类的对象,
实现接口内存分析
1. interface Singer{
2. public void sing();
3. public void sleep();
4. }
5. class Student implements Singer{
6. private String name;
7. Student(String name){
8. this.name=name;
9. }
10. public void study(){
11. System.out.println("studying");
12. }
13. public void sing(){
14. System.out.println("singing");
15. }
16. public void sleep(){
17. System.out.println("sleeping");
18. }
19. public String getName(){
20. return name;
21. }
22. }
23. public class Test{
24. public static void main(String args[]){
25. Singer S1=new Student("le");
26. s1.sing();
27. s1.sleep();
28. }
29. }
New出来的Student("le"),应该有sing();sleep();study();getName(),但是由于定义的是Singer S1,所以s1只能访问New出来的Student中的sing和sleep方法。
以前看设计模式,对Interface xx=new Object();及类似的语句理解非常模糊,类似这样的问题内存分析可以更直观的展示出其原理。本文简单介绍了函数、类、对象的内存分析,掌握这些可以更深刻的掌握面向对象常见的继承、实现、构造、多态等。
Java内存分配是Java的核心技术之一
◆寄存器:我们在程序中无法控制
◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
◆堆:存放用new产生的数据
◆静态域:存放在对象中用static定义的静态成员
◆常量池:存放常量
◆非RAM存储:硬盘等永久存储空间
java内存分配中的栈:
当一段代码中定义一个变量时,java就在栈中为这个变量分配内存空间,当该变量退出作用域后,java会自动释放掉该变量所分配的内存空间,该内存空间可以立即被另作他用。
java内存分配中的堆:
堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机的自动垃圾回收机制来管理。
--堆:Heap
--栈:Stack
---new一个对象时候存放在堆中
---变量存放在栈中
如:Student studnet = new Student() //student是对象句柄(引用)
栈的优势是,存取速度快,仅次于寄存器,栈中数据可以共享,缺点是栈的数据大小和生存期都必须确定,缺乏灵活性。
栈中主要存放一些基本类型的变量数据(int, short, long, byte, float, double, boolean, char)和对象句柄(引用)。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。
注意:
String str = "aaa";
String str = new String("aaa");
他们内存分配区别?
前者是在栈中,后者是在堆中创建对象,分配内存空间,其引用在栈中
String是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";
两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。
比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。
因此用第一种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。
另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。