首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

java内存储器机制_堆和栈

2012-10-26 
java内存机制_堆和栈???? System.out.println( s2s1.intern() )//true???? 这个类中事先没有声名”kvill

java内存机制_堆和栈
???? 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();?
???? String b = "a" + bb;?
???? System.out.println((a == b)); //result = false?
???? private static string getbb() {
?????? return "b";?
???? }
(5) String 变量采用连接运算符(+)效率低下。
???? String s = "a" + "b" + "c"; 就等价于String s = "abc";
???? String a = "a";
???? String b = "b";
???? String c = "c";
???? String s = a + b + c;
???? 这个就不一样了,最终结果等于:
?????? Stringbuffer temp = new Stringbuffer();
?????? temp.append(a).append(b).append(c);
?????? String s = temp.toString();
(6) Integer、Double等包装类和String有着同样的特性:不变类。
???? String str = "abc"的内部工作机制很有代表性,以Boolean为例,说明同样的问题。
???? 不变类的属性一般定义为final,一旦构造完毕就不能再改变了。
???? Boolean对象只有有限的两种状态:true和false,将这两个Boolean对象定义为命名常量:
???? public static final Boolean TRUE = new Boolean(true);
???? public static final Boolean FALSE = new Boolean(false);
???? 这两个命名常量和字符串常量一样,在常数池中分配空间。 Boolean.TRUE是一个引用,Boolean.FALSE是一个引用,而"abc"也是一个引用!由于Boolean.TRUE是类变量(static)将静态地分配内存,所以需要很多Boolean对象时,并不需要用new表达式创建各个实例,完全可以共享这两个静态变量。其JDK中源代码是:
???? public static Boolean valueOf(boolean b) {
????? ?return (b ? TRUE : FALSE);
???? }
???? 基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是JSE 5.0提供的新功能。 Boolean b1 = 5>3; 等价于Boolean b1 = Boolean.valueOf(5>3); //优于Boolean b1 = new Boolean (5>3);
??? static void foo(){
??????? boolean isTrue = 5>3;? //基本类型
??????? Boolean b1 = Boolean.TRUE; //静态变量创建的对象
??????? Boolean b2 = Boolean.valueOf(isTrue);//静态工厂
??????? Boolean b3 = 5>3;//自动装箱(autoboxing)
??????? System.out.println("b1 == b2 ?" +(b1 == b2));
??????? System.out.println("b1 == b3 ?" +(b1 == b3));
??????? Boolean b4 = new Boolean(isTrue);////不宜使用
??????? System.out.println("b1 == b4 ?" +(b1 == b4));//浪费内存、有创建实例的时间开销
??? } //这里b1、b2、b3指向同一个Boolean对象。
(7) 如果问你:String x ="abc";创建了几个对象?
???? 准确的答案是:0或者1个。如果存在"abc",则变量x持有"abc"这个引用,而不创建任何对象。
???? 如果问你:String str1 = new String("abc"); 创建了几个对象?
???? 准确的答案是:1或者2个。(至少1个在heap中)
(8) 对于int a = 3; int b = 3;
???? 编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
5、堆(Heap)和非堆(Non-heap)内存
???? 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”
???? 可以看出JVM主要管理两种类型的内存:堆和非堆。
???? 简单来说堆就是Java 代码可及的内存,是留给开发人员使用的;
???? 非堆就是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据) 以及方法和构造方法的代码都在非堆内存中。
堆内存分配
???? JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;
???? JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。
?????默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
???? 因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。
非堆内存分配
???? JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;
???? 由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。
例子
???? -Xms256m
???? -Xmx1024m
???? -XX:PermSize=128M
???? -XX:MaxPermSize=256M

?

?

6. Java中的析构方法finalize
??? finalize()方法常称之为终止器
????????? protected void finalize(){
????????????? // finalization code here
???????? }
??? 对象即将被销毁时,有时需要做一些善后工作。可以把这些操作写在finalize()方法里。
??? Java终止器却是在对象被销毁时调用。一旦垃圾收集器准备好释放无用对象占用的存储空间,它首先调用那些对象的finalize()方法,然后才真正回收对象的内存。而被丢弃的对象何时被销毁,应用是无法获知的。大多数场合,被丢弃对象在应用终止后仍未销毁。到程序结束的时候,并非所有收尾模块都会得到调用。

7. 应用能干预垃圾回收吗?
????在应用代码里控制JVM的垃圾回收运作是不可能的事。
??? 对垃圾回收有两个途径。第一个就是将指向某对象的所有引用变量全部移走。这就相当于向JVM发了一个消息:这个对象不要了。第二个是调用库方法 System.gc()。第一个是一个告知,而调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。
??? 希望JVM及时回收垃圾,是一种需求。其实,还有相反的一种需要:在某段时间内最好不要回收垃圾。要求运行速度最快的实时系统,特别是嵌入式系统,往往希望如此。
??? Java的垃圾回收机制是为所有Java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都不能命令垃圾回收机制做什么、怎么做或做多少。

热点排行