Java内存分配原理

Java内存分配一般涉及如下区域:
  • 寄存器:程序中无法控制
  • 栈:用于存放基本类型数据和对象的引用。
  • 堆:存放new产生的对象
  • 静态域:存放在对象中的用static定义的静态成员
  • 常量池:存放常量
  • 非RAM存储:硬盘等
栈:
创建一个变量即分配一个空间,当退出工作域时候,内存被自动释放。
堆:
存放new出的对象以及数组,有垃圾回收器管理。 栈中的变量指向了堆中的变量,这就是 Java指针
常量池:
指的是在编译期间被确定并保存在.class文件中的一些数据。除了包含代码中所定义的各种基本类型和对象的常量值(final),也还包含一些文本形式出现的符号引用,如:类和接口的全限定名,字段的名称和描述符,方法和名称的描述符。
虚拟机为每个被装载的类型维护一个常量池。对于String常量,他的值是在常量池中。JVM提供一个表来存储文字字符串值,不存引用。程序执行的时候,常量池会存储在方法区中,而不是堆中。
 
这里衍生出几个经常被问到的面试题:
String str = new String("abc");
String str2="abc";
这是创建字符串的两种方式,第一种是在堆中new一个对象。每次调用都会创建一个新的对象。而第二种是先在栈中创建一个String对象的引用地址。然后去常量池中寻找“abc”的存在与否,如果存在,则让那个引用指向“abc”,否则将“abc”放入常量池,并令引用指向“abc”。
 
再看:
  1. String s0="kvill";   
  2. String s1="kvill";   
  3. String s2="kv" + "ill";   
  4. System.out.println( s0==s1 );   
  5. System.out.println( s0==s2 ); 
输出结果会是什么呢?
答案是2个true。
首先我们要知道一个String常量只会有一个拷贝,s0和s1都是字符串常量,在编译期间就被确定,所以第一个是true
而kv与ill也都是字符串常量,在编译期间也被认为是字符串常量,所以s2也在常量池中kvill的一个引用,所以第二个也是true
 
再看看:
  1. String s0="kvill";   
  2. String s1=new String("kvill");   
  3. String s2="kv" + new String("ill");   
  4. System.out.println( s0==s1 );   
  5. System.out.println( s0==s2 );   
  6. System.out.println( s1==s2 );  
很多人可能会答错,正确答案是三个false
s0!=s1是肯定的,但是s0为什么!=s2呢?
因为s2的后半部分是new出来的,在运行期间才能被确定,因此合起来只能创建一个新对象在堆中,所以s0!=s2
 
String.intern()方法:当一个String实例调用intern时候,他会查找常量池中是否有相同Unicode值得字符串常量,有就返回其引用,若没就在常量池中创建一个相同的字符串并返回引用。
看例子:
  1. String s0= "kvill";   
  2. String s1=new String("kvill");   
  3. String s2=new String("kvill");   
  4. System.out.println( s0==s1 );   
  5. System.out.println( "**********" );   
  6. s1.intern();   
  7. s2=s2.intern(); //把常量池中"kvill"的引用赋给s2   
  8. System.out.println( s0==s1);   
  9. System.out.println( s0==s1.intern() );   
  10. System.out.println( s0==s2 );  
结果是:
false 
false //虽然执行了s1.intern(),但它的返回值没有赋给s1 
true //说明s1.intern()返回的是常量池中"kvill"的引用 
true
 
还有几个常见例子:
  1. String a = "a1";   
  2. String b = "a" + 1;   
  3. System.out.println((a == b)); //result = true  
  4. String a = "atrue";   
  5. String b = "a" + "true";   
  6. System.out.println((a == b)); //result = true  
  7. String a = "a3.4";   
  8. String b = "a" + 3.4;   
  9. System.out.println((a == b)); //result = true 
分析:JVM对于字符串常量的+号连接,在程序编译期,JVM就把常量字符串+连接优化为连接后的值,那“a”+1来说,两者都是在常量池中,因此编译器就确定为a1,所以结果是true
  1. String a = "ab";   
  2. String bb = "b";   
  3. String b = "a" + bb;   
  4. System.out.println((a == b)); //result = false
分析:bb是一个地址的引用,因此无法再编译期间就确定,所以为false
  1. String a = "ab";   
  2. final String bb = "b";   
  3. String b = "a" + bb;   
  4. System.out.println((a == b)); //result = true
bb被申明为常量,因此在编译期间就确定了,所以为true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值