专注收集记录技术开发学习笔记、技术难点、解决方案
网站信息搜索 >> 请输入关键词:
您当前的位置: 首页 > VB Dotnet

内存分配解决方法

发布时间:2011-06-23 16:01:34 文章来源:www.iduyao.cn 采编人员:星星草
内存分配
Java内存管理(一、内存分配)
关键字: 内存分配,常量池 
一、Java内存分配
1、 Java有几种存储区域?
* 寄存器
  -- 在CPU内部,开发人员不能通过代码来控制寄存器的分配,由编译器来管理
* 栈
  -- 在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域,即栈顶的地址和栈的最大容量是系统预先规定好的。
  -- 优点:由系统自动分配,速度较快。
  -- 缺点:不够灵活,但程序员是无法控制的。
  -- 存放基本数据类型、开发过程中就创建的对象(而不是运行过程中)
* 堆
  -- 是向高地址扩展的数据结构,是不连续的内存区域
  -- 在堆中,没有堆栈指针,为此也就无法直接从处理器那边获得支持
  -- 堆的好处是有很大的灵活性。如Java编译器不需要知道从堆里需要分配多少存储区域,也不必知道存储的数据在堆里会存活多长时间。
* 静态存储区域与常量存储区域
  -- 静态存储区用来存放static类型的变量
  -- 常量存储区用来存放常量类型(final)类型的值,一般在只读存储器中
* 非RAM存储
  -- 如流对象,是要发送到另外一台机器上的
  -- 持久化的对象,存放在磁盘上
2、 java内存分配
  -- 基础数据类型直接在栈空间分配;
  -- 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;
  -- 引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;
  -- 方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;
  -- 局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收;
  -- 方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间释放;
  -- 字符串常量在 DATA 区域分配 ,this 在堆空间分配;
  -- 数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!
3、Java内存模型
* Java虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method Area)、Java栈和Java堆。
  -- 方法区是静态分配的,编译器将变量在绑定在某个存储位置上,而且这些绑定不会在运行时改变。
  常数池,源代码中的命名常量、String常量和static 变量保存在方法区。
  -- Java Stack是一个逻辑概念,特点是后进先出。一个栈的空间可能是连续的,也可能是不连续的。
  最典型的Stack应用是方法的调用,Java虚拟机每调用一次方法就创建一个方法帧(frame),退出该方法则对应的 方法帧被弹出(pop)。栈中存储的数据也是运行时确定的?
  -- Java堆分配(heap allocation)意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。
  堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java对象的内存总是在heap中分配。
4、Java内存分配实例解析
  常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
  常量池在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。
  例:
  String s1=new String("kvill");
  String s2=s1.intern();
  System.out.println( s1==s1.intern() );//false
  System.out.println( s1+" "+s2 );// kvill kvill
  System.out.println( s2==s1.intern() );//true
  这个类中事先没有声名”kvill”常量,所以常量池中一开始是没有”kvill”的,当调用s1.intern()后就在常量池中新添加了一个”kvill”常量,原来的不在常量池中的”kvill”仍然存在。s1==s1.intern()为false说明原来的“kvill”仍然存在;s2现在为常量池中“kvill”的地址,所以有s2==s1.intern()为true。

 

String 常量池问题
(1) 字符串常量的"+"号连接,在编译期字符串常量的值就确定下来, 拿"a" + 1来说,编译器优化后在class中就已经是a1。
  String a = "a1";  
  String b = "a" + 1;  
  System.out.println((a == b)); //result = true 
  String a = "atrue";  
  String b = "a" + "true";  
  System.out.println((a == b)); //result = true 
  String a = "a3.4";  
  String b = "a" + 3.4;  
  System.out.println((a == b)); //result = true
(2) 对于含有字符串引用的"+"连接,无法被编译器优化。
  String a = "ab";  
  String bb = "b";  
  String b = "a" + bb;  
  System.out.println((a == b)); //result = false
  由于引用的值在程序编译期是无法确定的,即"a" + bb,只有在运行期来动态分配并将连接后的新地址赋给b。
(3) 对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝并存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。
  String a = "ab";  
  final String bb = "b";  
  String b = "a" + bb;  
  System.out.println((a == b)); //result = true
(4) jvm对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b。
  String a = "ab";  
  final String bb = getbb();  
友情提示:
信息收集于互联网,如果您发现错误或造成侵权,请及时通知本站更正或删除,具体联系方式见页面底部联系我们,谢谢。

其他相似内容:

热门推荐: