java不同数据类型在内存中的存储方式

转载自http://www.open-open.com/lib/view/open1415453057980.html

1、java的内存管理就是对象的分配和释放

分配:内存的分配是由程序完成的,程序员需要同关键字new为每个对象申请内存空间(基本类型除外),所有对象都是在堆(Heap)中分配空间。

释放:对象的释放是由垃圾回收机制决定和执行的,,这样做简化了程序员的工作,但是加重了JVM的工作,因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请,引用,被引用,赋值等,GC都需要进行监控。


2、内存泄漏(java内存泄漏解决方案

内存泄漏就是存在于一些被分配的对象,具有两特点,该内存对象的引用任然存在,该对象是无用,程序以后也不会使用的对象,这些对象就可以称之为java中的内存泄漏,这些对象不会被GC回收,但却占用内存。


3、JVM的内存区域组成(Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)


永久保存区:主要保存Class和Meta信息Class第一次被Load的时候被放入永久保存区,class需要存储的内容主要包括方法和静态属性。

栈内存:函数中定义的基本变量类型和对象的引用变量都在函数的栈内存中分配

堆内存:用来存放new出来的对象(只包含各自的成员变量,不包含成员方法。类的成员变量是存放在堆中的对象中的,基本类型和引用类型都在这个对象的空间中,作为一个整体存存储在堆中。同一个类中的不同对象拥有各自的成员变量,存储在各自的堆中,但是这些对象共享该类的方法,并不是没创建一个对象把成员方法复制一次)、数组以及对象的实例变量。


在函数中,定义变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放该变量所分配的内存空间。在堆中分配的内存是有JVM的自动垃圾回收器来管理。类中的方法是该类对象的共享的,对象使用方法时,方法在会被压入栈中,方法不使用不占用内存。



堆和栈的比较

堆:可以动态分配内存大小,生存期也不必事先告诉编辑器,因为它运行时动态分配内存的。但是正是由于运行时动态分配内存,所以存取速度比较慢。


栈:
栈数据可以共享,存取速度比较快。但是栈中的数据大小生成周期必须确定,缺乏灵活性。



4、java中的数据在内存中是如何存储

  • 基本数据类型

比如我们定义一个int数据时 int   a=3; 这里的a是一个指向int类型的引用,指向3这个字面值,这些字面值的数据是由大小可知,生存期可知(字面值定义在某个程序块,程序块推出后,字段值就消失,出于最求速度的原因,就存在于栈中)。


栈的数据共享,同时定义int a=3;int b=3;编译器先处理int =3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b这个引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。

  • 对象

在java中,创建一个对象包括对象的声明和实例化两步

<span style="font-size:14px;">public class Rectangle {
    double width;
    double height;

    public Rectangle(double w, double h) {
        w = width;
        h = height;
    }
}

</span>
声明对象时的内存模型:用Rectangle rect ,声明一个对象的rect,将栈内存为对象的引用变量rect 分配内存空间,但Rectangle值为null,称rect为一个空对象,空对象不能使用,因为它没有引用任何实体。

对象实例化是的内存模型:当执行rect=new Rectangle(3,5);时,会做两件事:在堆内存中为类的成员变量width,height分配内存,并将其初始化为各数据类型的默认值;接着进行显式初始化(类定义时的初始化值);最后调用构造方法,为成员变量赋值。返回堆内存中对象的引用(相当于首地址)给引用变量rect,以后就可以通过 rect来引用堆内存中的对象了。


  • 包装类

基本数据类型对应的包装类,与普通的对象没有区别。

String是特殊的包装类,可以通过以下两种方式创建

String s1 = new String();
String s2 = "abc";
第一种方式与普通对象的创建过程一样

第二种java内部将String s2 = "abc"划分为几个步骤:

1、先定义一个名为str的对String类的对象引用变量:String str;

2、在栈中查找有没有存放值为”abc”的地址,如果没有,则开辟一个存放字面值为”abc”地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈这个地址旁边记下这个引用的对象o。如果已经有了值为”abc”的地址,则查找对象o,并回o的地址。

3、将str指向对象o的地址。值得注意的是,一般String类中字符串值都是直接存值的。但像String str = “abc”;这种情况下,其字符串值却是保存了一个指向存在栈中数据的引用。

注:String中用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。


  • 数组

创建一个数组,存于堆中,在栈中创建一个引用,通过该引用操作数组。x=new int[3];将堆内存分配三个保存int数据的空间,堆内存的首地址放到栈内存中,每个数组元素被初始化为0。

  • 静态变量

用static修饰的变量和方法,实际上是指定这些变量和方法在内存中的“固定位置”(可以理解为所有对象的共有的内存空间),静态表示的是内存共享,就是它的每一个实例都指向同一个内存地址。使用static修饰,就是告诉JVM它是静态,它的引用都是指向同一个位置。

那静态变量与方法是在什么时候初始化的呢?对于两种不同的类属性,static属性与instance属性,初始化的时机是不同的。instance属性在创建实例的时候初始化,static属性在类加载,也就是第一次用到这个类的时候初始化,对于后来的实例的创建,不再次进行初始化。

public class Student {
	 static int a= 0;

	Student() {
		System.out.print(a++);
	}
	public static void main(String[] args) {
		Student s = new Student();
		Student s1 = new Student();
		Student s2 = new Student();
		Student s3 = new Student();
	}
}
输出结果:0123
每创建一个对象,a就会加一,因为a是static修饰的静态,static修饰的变量和方法是存储在所有对象的共有内存空间,每个实例都指向的同一个内存地址。

public class Student {
	int a= 0;

	Student() {
		System.out.print(a++);
	}
	public static void main(String[] args) {
		Student s = new Student();
		Student s1 = new Student();
		Student s2 = new Student();
		Student s3 = new Student();
	}
}
输出结果:0000

对比以上两次输出结果。


5、java内存管理实例

public class Dog {
	Collar c;
	String name;

	// 1.main()方法位于栈上
	public static void main(String[] args) {
		// 2.在栈上创建引用变量d,但Dog对象尚未存在
		Dog d;
		// 3.创建新的Dog对象,并将其赋予d引用变量
		d = new Dog();
		// 4.将引用变量的一个副本传递给go()方法
		d.go(d);
	}

	// 5.将go()方法置于栈上,并将dog参数作为局部变量
	void go(Dog dog) {
		// 6.在堆上创建新的Collar对象,并将其赋予Dog的实例变量
		c = new Collar();
	}

	// 7.将setName()添加到栈上,并将dogName参数作为其局部变量
	void setName(String dogName) {
		// 8.name的实例对象也引用String对象
		name = dogName;
	}
	// 9.程序执行完成后,setName()将会完成并从栈中清除,此时,局部变量dogName也会消失,尽管它所引用的String仍在堆上
}
以下部分参阅原博客文章。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值