`
xmong
  • 浏览: 259001 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

java内存管理

阅读更多
java内存管理

1 Java内存
1.1 Java有几种存储区域
 寄存器:位于CPU内部,是最快的存储器,开发人员不能通过代码来控制寄存器的分配,由编译器来管理。
 栈:位于通用RAM(随机访问存储器)中,是一块连续的内存的区域,通过栈指针可以从处理器那里获得支持,指针想下移动,则分配新内存;向上移动,则释放内存。在java中用于存放基本数据类型和对象的引用。
 堆:一种通用的内存池(也位于RAM中),是不连续的内存区域。在java中用于存放所有的java对象。
 常量存储区与静态存储区:常量存储区用来存放常量类型(final)类型的值,一般在只读存储器中,静态存储区用来存放static类型的变量。
 非RAM存储:如果数据完全活于程序之外,那么它可以不受程序的任何控制,在程序没有运行时也可以存在。如对象流,持久化对象。

1.2 Java内存分配方式
每个java虚拟机都有一个类装载器子系统,他根据给定的全限定名来装入类型(类或接口)。同样每个java虚拟机都一个执行引擎,他负责执行那些包还在被装载类的方法中的指令。当java虚拟机运行程序时,需要分配内存来存储许多东西,如创建的对象,传递方法的参数,返回值等。这时候需要对内存进行相应的分配,内存的分配可以通过三种方式:
静态分配:这是最简单的内存分配策略,将程序中的所有名字在编译时绑定在某个存储位置上,这种绑定不会在运行时改变。
常数池,源代码中的命名常量、String常量和static 变量保存在方法区。
栈分配:块结构语言通过在栈上分配内存,克服了静态分配的一些限制。每次过程调用时,一个活动记录或是帧被压入系统栈,并在返回时弹出。
最典型的Stack应用是方法的调用,Java虚拟机每调用一次方法就创建一个方法帧(frame),退出该方法则对应的  方法帧被弹出(pop)。栈中存储的数据也是运行时确定的
堆分配:堆中的数据结构能够以任意次序分配和释放。意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。因而活动记录和动态数据结构可能比创建他们的过程更长寿。
堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java对象的内存总是在heap中分配。

1.3 Java数据类型的内存分配
     -- 基础数据类型直接在栈空间分配;
     -- 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;
     -- 引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;
     -- 方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;
     -- 局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收;
     -- 方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间释放;
     -- 字符串常量在 DATA 区域分配 ,this 在堆空间分配;
     -- 数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小

2 实例分析
2.1 常量池
常量池(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。

2.2 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的情况。

2.3 堆(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





---------------------------------------


参考:
《thinking in java》
java内存管理机制文档


分享到:
评论

相关推荐

    Java内存管理机制相关资料汇总

    资源名称:Java内存管理机制相关资料汇总资源目录:【】java内存回收机制及预防【】java内存管理机制【】java内存管理白皮书【】Java虚拟机内存管理_对象和引用_空指针【】深入理解java虚拟机jvm高级行与最佳实践...

    Java内存管理机制

    1、JAVA 内存管理总结 2、Java的内存管理实例 3、垃圾回收机制:

    java内存管理精彩概述

    java内存管理精彩概述 java内存管理精彩概述 java内存管理精彩概述

    java内存管理详细介绍.doc

    本文档主要讲述的是java内存管理详细介绍;对于从事C、C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象...

    java内存管理 .pptx

    java内存管理

    java内存管理总结

    java 内存管理 详细介绍了java内存管理各个方面的内容

    关于java内存管理的一些理解

    主要关于java虚拟机的运行时数据区域,参考了周志明的深入理解java虚拟机,还涉及到了native方法、垃圾回收机制等等。

    java内存管理白皮书-英文版

    One strength of the Java™ 2 Platform, Standard Edition (J2SE™) is that it performs automatic memory management, thereby shielding the developer from the complexity of explicit memory management.

    JAVA 内存管理总结

    JAVA 内存管理总结 1. java是如何管理内存的 2. 什么叫java的内存泄露 3. JVM的内存区域组成 4.Java中数据在内存中是如何存储的 5. Java的内存管理实例

    java内存管理以及GC

    内存管理简介 内存管理的职责为分配内存,回收内存。 没有自动内存管理的语言/平台容易发生错误。 典型的问题包括悬挂指针问题,一个指针引用了一个已经被回收的内存地址,导致程序的运行完全不可知。 另一个...

    java内存管理详细介绍

    java内存管理详细介绍,增强自己对虚拟机和内存进一步认识!

    java内存管理深入讲解

    java内存管理深入讲解 基本是理解java程序内存的直接能用的资料了

    Java的内存管理机制分析

    Java的内存管理机制分析 让你了解java的内存管理 以及如何去分析它

    java内存管理 ppt

    java内存结构,垃圾收集,并发收集,虚拟机参数

    java内存管理与垃圾回收

    java内存管理与垃圾回收

    JAVA内存管理机制

    JAVA内存管理机制的思维导图

    JAVA内存管理模式研究.pdf

    JAVA内存管理模式研究

Global site tag (gtag.js) - Google Analytics