【经典】用栈和堆来理解 String。好文章:String 必须要理解!!!

来源:互联网 发布:上古卷轴5低配优化 编辑:程序博客网 时间:2024/05/19 03:45

一、两种实例化方式:

String str = “abc”;

String str = new String("abc");

一个字符串就是String的匿名对象。

"hello".equals(str)    一个字符串能够调用一个函数,可以看出,一个字符串是String的匿名对象。

二、比较

(1)直接赋值

String str = “abc”;表示一个堆内存指向给了栈内存。

直接赋值可以节省内存

举例:

[java] view plaincopy
  1. public class StringDemo01{  
  2.     public static void main(String args[]){  
  3.         //享元模式  
  4.         //在堆内存中有一个"hello"的String对象,而str1和str2和str3指向这个匿名对象  
  5.         String str1 = "hello";  
  6.         String str2 = "hello";  
  7.         String str3 = "hello";  
  8.         System.out.println("str1 == str2  --> "+(str1 == str2));   
  9.         System.out.println("str1 == str3  --> "+(str1 == str3));   
  10.         System.out.println("str2 == str3  --> "+(str2 == str3));   
  11.     }  
  12. }  


(2)new

举例:

[java] view plaincopy
  1. public class StringDemo01{  
  2.     public static void main(String args[]){  
  3.           
  4.         String str1 = new String("hello");  
  5.         String str2 = new String("hello");  
  6.         System.out.println("str1==str2 -->"+(str1==str2));   
  7.     }  
  8. }  

 


 

1.创建了"hello"的匿名String对象。

2.创建一个str的对象,并开辟新的堆空间。

3.原来的“hello”的匿名对象变成垃圾。

 

因此开发中最好使用直接赋值。

 

三、字符串的不可改变性


为什么str+="world"这种连接操作效率低,就是因为这个。

所以才会有StringBuilder或者StringBuffer。

 

四、String和StringBuilder的性能比较

 

[java] view plaincopy
  1. public class StringDemo02{  
  2.     public static void main(String args[]){  
  3.         long begin1 = System.currentTimeMillis();  
  4.         String str = "";  
  5.         for(int i=0;i<10000;i++){  
  6.             str+=i;  
  7.         }  
  8.         long end1 = System.currentTimeMillis();  
  9.         System.out.println("String用时: "+(end1-begin1)+" ms");    //用时2000ms  
  10.           
  11.   
  12.         long begin2 = System.currentTimeMillis();  
  13.         StringBuilder sbuilder = new StringBuilder();  
  14.         for(int i=0;i<10000;i++){  
  15.             sbuilder.append(i);  
  16.         }  
  17.         long end2 = System.currentTimeMillis();  
  18.         System.out.println("StringBuilder用时: "+(end2-begin2)+" ms");//用时0ms,所以快了很多  
  19.     }  
  20. }  

===========================================================================================

String str=new String("x"); 
和String str="x"; 

这两句有什么区别?

在计算机的内存中有堆和栈两块空间,其中栈中存放所有对象、函数、变量等的饮用;而堆中存放对象、值等。 
不管是new出的对象还是用类似String str = "x";这类的对象,都是放在堆中的。 
对于你的第一个问题,举一个简单的例子: 

String str1 = "x" ; String str2 = "x" ; String str3 = new String("x") ; 


表面上看这三句回实例化3个String对象,实际上是两个。再java中有一个叫“字符数据池”的内存管理机制,在对str2赋值之前,会检查池中有没有相同的数据,如果有,则直接引用;若没有,系统会实例化这个String的对象;而执行到String str3 = new String("x") ;系统会直接实例化该对象,而不会在池中查找。也不会将这个对象放在池中。
你可能会问,如果改动str1或str2时会不会改动另外一个引用的值。答案是不会。系统会首先在池中查找有没有相同的对象,如果没有,会实例化这个对象。  

public static void main(String[] arg) {
  String b = "1";
  // String a=new String("a");
  int c = 1;
  String a = String.valueOf(c); // a!=b
  // String a= String.valueOf("1"); //a=b
  if (a == b) {
   System.out.print("a");

  }
 }




http://fluagen.blog.51cto.com/146595/78018


        1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 


  2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。


  3. Java中的数据类型有两种。

  一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。

  另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:

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

  特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

  另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 

4. String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用String str = "abc";的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = "abc";中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。


  5. 关于String str = "abc"的内部工作。Java内部将此语句转化为以下几个步骤:

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

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

  (3)将str指向对象o的地址。

  值得注意的是,一般String类中字符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!


  为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。

String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
  注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。
结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。


  我们再来更进一步,将以上代码改成:

String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
System.out.println(str1 + "," + str2); //bcd, abc
System.out.println(str1==str2); //false
  这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。

  事实上,String类被设计成为不可改变(immutable)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良影响。


  再修改原来代码:

String str1 = "abc";
String str2 = "abc";

str1 = "bcd";

String str3 = str1;
System.out.println(str3); //bcd

String str4 = "bcd";
System.out.println(str1 == str4); //true

   str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。

当str1改完其值后,再创建一个String的引用str4,并指向因str1修改值而创建的新的对象。

可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。


  我们再接着看以下的代码。

String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false
  创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。

String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1==str2); //false

  创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。

  以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。


  6. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。
7. 结论与建议:

  (1)我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。

  (2)使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。

  
(3)当比较包装类里面的数值是否相等时,用equals()方法;
当测试 两个包装类的引用 是否指向同一个对象时,用==。

  (4)由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

 
 
 
------------------------------------------------------------------------------------------
 
String在栈中,StringBuffer在堆中!所以String是不可变的,数据是共享的。StringBuffer都是独占的,是可变的(因为每次都是创建新的对象!)
 
JAVA中都是传的引用。
我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。)
 
这里String str 是一个String类的引用,所以在用诸如swap(String str1,String str2){}之类的方法时传的都是引用。
------------------------------------------------------------------
 
 
是这样的,JAVA为了提高效率,所以对于String类型进行了特别的处理---为string类型提供了串池 
定义一个string类型的变量有两种方式: 
string name= "tom "; 
string name =new string( "tom ") 
使用第一种方式的时候,就使用了串池, 
使用第二中方式的时候,就是一种普通的声明对象的方式 
如果你使用了第一种方式,那么当你在声明一个内容也是 "tom "的string时,它将使用串池里原来的那个内存,而不会重新分配内存,也就是说,string saname= "tom ",将会指向同一块内存 


另外关于string类型是不可改变的问题: 
string类型是不可改变的,也就是说,当你想改变一个string对象的时候,比如name= "madding " 
那么虚拟机不会改变原来的对象,而是生成一个新的string对象,然后让name去指向它,如果原来的那个 "tom "没有任何对象去引用它,虚拟机的垃圾回收机制将接收它。 
据说这样可以提高效率!!!哈哈 

 
----------------------------------------------------------------------------------------------------------------
 
public class Pass { 
String a="123"; 

public static void test(Pass passA) { 
passA.a="abc"; 

public static void main(String[] args) { 
Pass passB=new Pass(); 
passB.a= "123"; 
System.out.println(passB.a); 
test(passB); 
System.out.println(passB.a); 


结果是: 
123 
abc 
从这个结果来看是通过引用传递的.String不是简单类型,那么是不是也是通过引用来传递呢?看下面这个例子: 
public class Pass { 
public static void test(String str) { 
str = "World"; 

public static void main(String[] args) { 
String string = "Hello"; 
System.out.println(string); 
test(string); 
System.out.println(string); 


结果是: 
Hello 
Hello 
第三个例子: 
public class Pass { 
public static void test(StringBuffer str) { 
str.append("World"); 

public static void main(String[] args) { 
StringBuffer string = new StringBuffer("Hello"); 
System.out.println(string); 
test(string); 
System.out.println(string); 


结果: 
Hello 
HelloWorld 
这我就不太明白了,既然String不是简单类型,那么它应该和对象是一样通过引用来传递的,可是结果却相反.而StringBuffer却是通过引用来传递的? 
这到底是为什么?哪位帮忙解释一下.
 
----------------------------------------------------------------------------------


所有的参数传递都是 传值,从来没有 传引用 这个事实。(
从程序运行的角度来看,参数传递,只有传值,从不传递其它的东西。只不过,值的内容有可能是数据,也有可能是一个内存地址
程序运行的时候,使用的空间可以分为两个部分,栈和堆。栈是指运行栈,局部变量,参数,都分配在栈上。程序运行的时候,新生成的对象,都分配在堆里,堆里分配的对象,栈里的数据参数,或局部变量。
)
 

所有的参数传递都会在 程序运行栈上 新分配一个 值 的复制品. 

楼主的第一段代码。 

public static void test(Pass passA) { 
passA.a="abc"; 



这个传的 PassA 的 地址值。这个 地址值 被复制了一份。 
不信,你写: 

public static void test(Pass passA) { 
passA = null; 



看看, 对passA有什么影响? 
毫无作用。函数调用出来后,passA还是原来的值,不会变成Null. 

但是,你的代码对 passA进行了操作 passA.a ,改变了passA的成员变量。 
这个成员变量是一个真实指向String 的 地址,当然能够被改变。 
这就是操作 (.) 和 赋值 (=) 的区别。 
这是对 成员变量 a 的 赋值。真正改变了成员变量 a 的值。 

注意,这里传递的参数是 passA, 而不是 a. 
所以,passA 被复制了一份。passA 的这个副本的 a 变量还 指向 原来的 passA 的 a 变量。 


public static void test(String str) { 
str = "World"; 



只有对参数的 赋值,没有对参数的操作,当然不会产生影响。

================================================================================

栈内存

堆内存

基础类型,对象引用(堆内存地址)

由new创建的对象和数组,

存取速度快

相对于栈内存较慢

数据大小声明周期必须确定

分配的内存由java虚拟机自动垃圾回收器管理。动态分配内存大小

共享特性

栈中如果有字符串,则直接引用

如果没有,开辟新的空间存入值

每new一次在堆内存中生成一个新的对象。

创建之后值可以改变

String类声明后则不可改变    

一、栈内存

基础类型int, short, long, byte, float, double, boolean, char和对象引用

 

栈的共享特性

String str1 = "abc"; 
String str2 = "abc"; 
System.out.println(str1==str2); //true

1、编译器先处理String str1 = "abc";它会在栈中创建一个变量为str1的引用,然后查找栈中是否有abc这个值,如果没找到,就将abc存放进来,然后将str1指向abc。

2、   接着处理String str2 = "abc";在创建完b的引用变量后,因为在栈中已经有abc这个值,便将str2直接指向abc。这样,就出现了str1与str2同时均指向abc的情况。

二、堆内存

new、newarray、anewarray和multianewarray等指令建立

   要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的 对象。只有通过new()方法才能保证每次都创建一个新的对象。 由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

三、  ==   内存地址比对

String str1 = "abc"; 
String str2 = "abc"; 
System.out.println(str1==str2); //true    str1和str2同时指向 栈内存 中同一个内存空间

String str3 = "abc"; 
String str4 = new String("abc") ;

System.out.println(str3 == str4);    //flase str3值在栈内存中,str4值在堆内存中

 

String hello = "hello" ;

String hel = "hel" ;

String lo = "lo" ;

System.out.println(hello == "hel" + "lo") ; //true

//两个常量相加,先检测栈内存中是否有hello如有有,指向已有的栈中的hello空间

System.out.println(hello == "hel" + lo) ;   //flase

System.out.println(hello == hel + lo) ;     //flase

 //lo是在常量池中,不检查栈内存,在堆中产生一个新的hello

 

 四、  equals  值进行比对

 public boolean equals(Object anObject)

将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true。

 String str5 = "abc"; 
String str6 = new String("abc") ;

System.out.println(str5.equals(str6));    //true   str5的值str6的值比对

 五、  intern    栈中值的内存地址

 

Public String intern()

当调用 intern 方法时

1、如果池已经包含一个等于此 String 对象的字符串(用equals(Object) 方法确定),则返回池中的字符串。

2、将此 String 对象添加到池中,并返回此 String 对象的引用。

 

String s7 = new String("abc") ;

String s8 = "abc" ;

 

System.out.println(s7 == s7.intern()) ;//flase;

System.out.println(s8 == s7.intern() );//true

 

1.检查栈内存中有没有abc对象如果有

2.将s7指向pool中abc

===================================================================================================

http://sd.csdn.net/a/20120220/311988.html                       Java 堆内存的十个要点

导读:对于程序员来说,知道堆空间,设置堆空间,处理堆空间的outOfMemoryError错误,分析heap dump是非常重要的。文中介绍了Java堆的学习教程以及Java堆内存(heap memory)的十个要点。

文章内容如下:

我刚开始学习Java编程时,可不知道什么是堆内存或堆空间(heap space),甚至根本不管对象创建时都放在哪里去了。正式了写一些程序后,经常会遇到java.lang.outOfMemoryError等错误,我才开始关注堆内存。

对大多数程序员都经历过这样的过程,因为学习一种语言是非常容易来的,但是学习基础是非常难的,因为没有什么特定的流程让你学习编程的每个基础,使你发觉编程的秘诀。

对于程序员来说,知道堆空间,设置堆空间,处理堆空间的outOfMemoryError错误,分析heap dump是非常重要的。这个关于Java堆的教程是给我刚开始学编程的兄弟看的。如果你知道这个基础知识或者知道底层发生了什么,当然可能帮助不是那么大。除非你知道了对象被创建在堆中,否则你不会意识到OutOfMemoryError是发生在堆空间中的。我尽可能的将我所知道的所有关于堆的知识都写下来了,也希望你们能够尽可能多的贡献和分享你的知识,以便可以让其他人也受益。

Java中的堆空间是什么?

当Java程序开始运行时,JVM会从操作系统获取一些内存。JVM使用这些内存,这些内存的一部分就是堆内存。堆内存通常在存储地址的底层,向上排列。当一个对象通过new关键字或通过其他方式创建后,对象从堆中获得内存。当对象不再使用了,被当做垃圾回收掉后,这些内存又重新回到堆内存中。要学习垃圾回收,请阅读”Java中垃圾回收的工作原理”。

如何增加Java堆空间

在大多数32位机、Sun的JVM上,Java的堆空间默认的大小为128MB,但也有例外,例如在32未Solaris操作系统(SPARC平台版本)上,默认的最大堆空间和起始堆空间大小为 -Xms=3670K 和 -Xmx=64M。对于64位操作系统,一般堆空间大小增加约30%。但你使用Java 1.5的throughput垃圾回收器,默认最大的堆大小为物理内存的四分之一,而起始堆大小为物理内存的十六分之一。要想知道默认的堆大小的方法,可以用默认的设置参数打开一个程序,使用JConsole(JDK 1.5之后都支持)来查看,在VM Summary页面可以看到最大的堆大小。

用这种方法你可以根据你的程序的需要来改变堆内存大小,我强烈建议采用这种方法而不是默认值。如果你的程序很大,有很多对象需要被创建的话,你可以用-Xms and -Xmx这两个参数来改变堆内存的大小。Xms表示起始的堆内存大小,Xmx表示最大的堆内存的大小。另外有一个参数 -Xmn,它表示new generation(后面会提到)的大小。有一件事你需要注意,你不能任意改变堆内存的大小,你只能在启动JVM时设定它。

堆和垃圾回收

我们知道对象创建在堆内存中,垃圾回收这样一个进程,它将已死对象清除出堆空间,并将这些内存再还给堆。为了给垃圾回收器使用,堆主要分成三个区域,分别叫作New Generation,Old Generation或叫Tenured Generation,以及Perm space。New Generation是用来存放新建的对象的空间,在对象新建的时候被使用。如果长时间还使用的话,它们会被垃圾回收器移动到Old Generation(或叫Tenured Generation)。Perm space是JVM存放Meta数据的地方,例如类,方法,字符串池和类级别的详细信息。你可以查看“Java中垃圾回收的工作原理”来获得更多关于堆和垃圾回收的信息。

java

Java堆中的OutOfMemoryError错误

当JVM启动时,使用了-Xms 参数设置的对内存。当程序继续进行,创建更多对象,JVM开始扩大堆内存以容纳更多对象。JVM也会使用垃圾回收器来回收内存。当快达到-Xmx设置的最大堆内存时,如果没有更多的内存可被分配给新对象的话,JVM就会抛出java.lang.outofmemoryerror,你的程序就会当掉。在抛出 OutOfMemoryError之前,JVM会尝试着用垃圾回收器来释放足够的空间,但是发现仍旧没有足够的空间时,就会抛出这个错误。为了解决这个问题,你需要清楚你的程序对象的信息,例如,你创建了哪些对象,哪些对象占用了多少空间等等。你可以使用profiler或者堆分析器来处理 OutOfMemoryError错误。”java.lang.OutOfMemoryError: Java heap space”表示堆没有足够的空间了,不能继续扩大了。”java.lang.OutOfMemoryError: PermGen space”表示permanent generation已经装满了,你的程序不能再装在类或者再分配一个字符串了。

Java Heap dump

Heap dump是在某一时间对Java堆内存的快照。它对于分析堆内存或处理内存泄露和Java.lang.outofmemoryerror错误是非常有用的。在JDK中有一些工具可以帮你获取heap dump,也有一些堆分析工具来帮你分析heap dump。你可以用“jmap”来获取heap dump,它帮你创建heap dump文件,然后,你可以用“jhat”(堆分析工具)来分析这些heap dump。

Java堆内存(heap memory)的十个要点:

1. Java堆内存是操作系统分配给JVM的内存的一部分。

2. 当我们创建对象时,它们存储在Java堆内存中。

3. 为了便于垃圾回收,Java堆空间分成三个区域,分别叫作New Generation, Old Generation或叫作Tenured Generation,还有Perm Space。

4. 你可以通过用JVM的命令行选项 -Xms, -Xmx, -Xmn来调整Java堆空间的大小。不要忘了在大小后面加上”M”或者”G”来表示单位。举个例子,你可以用 -Xmx256m来设置堆内存最大的大小为256MB。

5. 你可以用JConsole或者 Runtime.maxMemory(), Runtime.totalMemory(), Runtime.freeMemory()来查看Java中堆内存的大小。

6. 你可以使用命令“jmap”来获得heap dump,用“jhat”来分析heap dump。

7. Java堆空间不同于栈空间,栈空间是用来储存调用栈和局部变量的。

8. Java垃圾回收器是用来将死掉的对象(不再使用的对象)所占用的内存回收回来,再释放到Java堆空间中。

9. 当你遇到java.lang.outOfMemoryError时,不要紧张,有时候仅仅增加堆空间就可以了,但如果经常出现的话,就要看看Java程序中是不是存在内存泄露了。

10. 请使用Profiler和Heap dump分析工具来查看Java堆空间,可以查看给每个对象分配了多少内存。











原创粉丝点击