`
endual
  • 浏览: 3565845 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java内存中的对象

    博客分类:
  • java
 
阅读更多

痴情研究java内存中的对象

                            痴情研究java内存中的对象
前记:
几天前,在浏览网页时偶然的发现一道以前就看过很多遍的面试题,题目是:“请说出‘equals’和‘==’的区别”,当时我觉得我还是挺懂的,在心里答 了一点(比如我们都知道的:‘==’比较两个引用是否指向同一个对象,‘equals’比较两个对象的内容),可是总觉得心里有点虚虚的,因为这句话好像 太概括了,我也无法更深入地说出一些。于是看了几篇别人的技术博客,看完后我心里自信地说,我是真的懂了;后来根据我当时的理解,就在eclipse中敲 了些代码验证一下,发现有些运行的结果和我预期的又不一样,怎么找原因都找不到,呵呵~,这时就感觉太伤自尊了,于是我觉得我真的还是不懂得,又去上网查 找答案,呵呵,这个问题花了我整整三天的时间,现在想把我的一些总结写下来,以达到检测自己的目的,也欢迎大家浏览、批评、指正。
注:本文不仅研究类类型的对象,还研究基本数据类型

 

线索:
我想采用实例代码驱动的方式来一步步地分析,这也符合我们探知新事物的过程。

 

一、基本数据类型的内存分配


代码1:

Java代码  收藏代码
  1. int  p1= 1000 ;  
  2. static    int  p2= 1000 ;  
  3. public   void  myTest(){  
  4. System.err.println("****************Integer*********************" );   
  5. int  i1= 1000 ;  
  6. int  i2= 1000 ;  
  7. Integer i3=1000 ;  
  8. Integer i4=1000 ;  
  9. Integer i5=100 ;  
  10. Integer i6=100 ;  
  11. Integer i7=new  Integer( 1000 );  
  12. Integer i8=new  Integer( 1000 );   
  13. System.err.println(i1==p1);  //true(输出结果)   1(编号,便于分析)   
  14. System.err.println(i1==p2);  //true                  2   
  15. System.err.println(i1==i2);  //true                   3   
  16. System.err.println(i3==i4);  //false                  4   
  17. System.err.println(i5==i6);  //true                   5   
  18. System.err.println(i7==i8);  //false                  6   
  19. System.err.println(i1==i3);  //true                   7   
  20. System.err.println(i1==i7);  //true                   8   
  21. System.err.println(i3==i7);  //false                  9   
  22. System.err.println("****************Integer*********************" );  
  23. }  

 

看到上面的输出结果,如果你还是有些不能理解的,那就耐心地接着看我的分析吧。

 

分析:


编号1:在java编译时期,当编译到“int p1=1000; ”时会创建常量1000,放入常量池,其实后面的p2,i1,i2都是指向这个1000,这样可以提高java的性能,所以编号1、编号2、编号3的输出 结果都是true.其实char,float,double等基本数据类型都是这样的。


编号2、编号3:同编号1


编号4:这是java中的自动装箱机制,将基本数据类型int自动转为 类类型Integer,这是jdk1.5以上才有的功能,jdk1.5以下编译时会报错。自动装箱时java底层会调用 Integer.valueOf(int i)方法自动装箱,下面我们来看看Integer.valueOf(int i)的源码吧:

Java代码  收藏代码
  1. /**    
  2. * @param  i an <code>int</code> value.  
  3.      * @return a <tt>Integer</tt> instance representing <tt>i</tt>.  
  4.      * @since  1.5  
  5.      */   
  6.     public   static  Integer valueOf( int  i) {  
  7.         if (i >= - 128  && i <= IntegerCache.high)  
  8.             return  IntegerCache.cache[i +  128 ];  
  9.         else   
  10.             return   new  Integer(i);  
  11.     }  

 

注:分析源码我们知道IntegerCache.high其实就是127,在IntergerCache的静态块中定义的。

 

源码的意思是当i的值在-128—127之间时会返回 IntegerCache.cache[]中的对象,其他的新建一个Integer对象。其实Integer类是这样实现的:考虑到-128—127之间 的对象经常使用,就在Integer创建时将值在-128—127之间的对象先创建好,放在池中,以后要使用时,这些对象就不用重新创建了,目的在于提高 性能。其实这种机制在Character中也用到了,Character是创建ASCII在0—127之间的对象。补充说明:Integer创建的对象引 用在栈中,对象的内容在堆区,栈中的值是堆中对象的地址。Character、Long、Short等包装类都是这样的。所以编号4的输出结果是 false,因为值大于127,java新创建了一个对象。

 

编号5:因为值在-128—127之间,所以两个引用指向的是堆区的同一个对象。


编号6:当使用new创建对象时,都会新创建一个对象,即在栈中创建一个引用,在堆中创建该对象,引用指向对象。

 

编号7:这种情况有些人可能会不太清楚,其实这是java的自动拆箱机制,当 int和Integer发生操作时,Integer类型对象会自动拆箱成int值,这时比较的是两个int值,而我们前面分析了,int值都会指向常量池 中的数据,所以,两者指向的是同一块空间。结果编号7输出true


编号8:同编号7,也是Integer的自动拆箱。

 

编号9:我想,分析了这么多,编号9不用我说,你也应该懂了,呵呵,这里就不赘述了哦~
分析了这么多,终于第一块代码分析完了。

 

二、String类型的内存分配


大家都知道String类型是类类型,不过String类型是一个特殊的类类型,那它特殊在哪呢?
代码2:

Java代码  收藏代码
  1.        System.err.println( "****************string*********************" );     
  2.  String s1="abc" ;  
  3.  String s2="abc" ;  
  4.  String s3=new  String( "abc" );  
  5.  String s4=new  String( "abc" );  
  6.  System.err.println(s1==s2);//true (输出结果) 1(编号)   
  7.  System.err.println(s3==s4);//false                   2   
  8.  System.err.println(s1==s3);//false                    3   
  9.    
  10.  String a = "abc" ;     
  11. String b = "ab" ;     
  12. String c = b + "c" ;    
  13. System.err.println(a==c);//false                         4   
  14.   
  15.  String s5 = "123" ;   
  16.  final  String s6= "12" ;  
  17.  String s7=s6+"3" ;  
  18.  System.err.println(s5 == s7);//true                     5                 
  19.  System.err.println("****************string*********************" );  

 

编号1:String类型是一个很特殊的类型,当我们使用String str=”abc”;这种定义方法时,”abc”会放入常量池中,以后如果再有定义String str2=”abc”时,其实str和str2指向的是常量池中同一个对象。而只有当使用new创建时才会每次都创建一个新的对象。(我觉得这是 String类型和其他类类型的特殊之处)


编号2、编号3:编号1已经分析了。


编号4:执行到  String c = b + "c"; 这一句时,java底层会先创建一个StringBuilder对象,封装b,接着再加上“c”,最后再创建一个String对象,将 StringBuilder中的值赋给该String对象,用c来指向它。.其实此时的c指向的对象已经不是a指向的对象了。


编号5:当用final修饰后,s6就变为了常量,在常量池中创建“12”,当执行到String s7=s6+"3";时,编译器直接就把s6当成了“12”,s7此时就已是“123”,它指向常量池中的“123”,所以s5和s7指向的是同一个对象,输出为true。

 

三、StringBuilder,StringBuffer,String的对比


   (一)String
String类型的值是不可变的,听到这句话后可能你会有疑问,我们的String对象可以重新赋值呀,这里有两种情况,情况一:String str=”abc”; , 情况二:String str=new String(“abc”);采用情况一重新赋值时,java会先看常量池中有没有“abc”,如果有则直接指向它,如果没有,在编译时就创建一个常量放 入常量池中;对于情况二:str则重新指向一个先创建的对象,该新对象在堆中。下面提出问题:为什么String是不可变的呢?我们来看看String的 源码:

 

Java代码  收藏代码
  1. public   final   class  String  
  2.     implements  java.io.Serializable, Comparable<String>, CharSequence  
  3. {  
  4.     /** The value is used for character storage. */   
  5.     private   final   char  value[];  
  6.   
  7.     /** The offset is the first index of the storage that is used. */   
  8.     private   final   int  offset;  
  9.   
  10.     /** The count is the number of characters in the String. */   
  11.     private   final   int  count;  
  12.   //••••••••••••••••••••   

 

我们看到String类型是用一个用final修饰的char数组来存储字符串 的,所以String类型是不可变的,(其实Short,Character,Long等包装类型也是这样实现的),根据上面对String类型的分析, 如果要改变String的值,就要重新创建一个对象,这无疑性能会很差。为了优化String,sun公司添加了StringBuffer,在 jdk1.5之后又添加了StringBuilder。


(二)下面我们来分析一下StringBuffer
StringBuffer作为字符串缓冲类,当进行字符串拼接时,不会重新创建一个StringBuffer对象,而是直接在原有值后面添加,因为 StringBuffer类继承了AbstractStringBuffer类,分析后者的源码后,我们发现存储字符串的char[]没有被final修 饰。至于StringBuffer类是怎样扩充自己的长度的,我们可以参考它的append()方法,这里不再赘述。不过一定要提出的 是:StringBuffer是线程安全的,它的方法体是被synchronized修饰了的。

 

(三)StringBuilder有是怎么样的呢?
StringBuilder基本实现了StringBuffer的功能,最大的不同之处在于StringBuilder不是线程安全的。


(四)String、StringBuffer、StringBuilder的性能比较
代码三:


Java代码  收藏代码
  1.               StringBuffer b =  new  StringBuffer( "abc" );  
  2. long  t3 = System.currentTimeMillis();  
  3. for  ( int  i =  0 ; i <  1000000 ; i++) {  
  4.     b = b.append("def" );  
  5. }  
  6. long  t4 = System.currentTimeMillis();  
  7. System.out.println("1000000次拼接,StringBuffer所花时间为:"  + (t4 - t3));  
  8. System.out.println("*************************************" );  
  9. StringBuilder c = new  StringBuilder( "abc" );  
  10. long  t5 = System.currentTimeMillis();  
  11. for  ( int  i =  0 ; i <  1000000 ; i++) {  
  12.     c = c.append("def" );  
  13. }  
  14. long  t6 = System.currentTimeMillis();  
  15. System.out.println("1000000次拼接,StringBuilder所花时间为:"  + (t6 - t5));  
  16. System.out.println("*************************************" );  
  17. String S1 = "abc" ;  
  18. long  t1 = System.currentTimeMillis();  
  19. for  ( int  i =  0 ; i <  10000 ; i++) {  
  20.     S1 += "def" ;  
  21. }  
  22. long  t2 = System.currentTimeMillis();  
  23. System.out.println("10000次拼接,String所花时间为:"  + (t2 - t1));  

 

实验结果为:

 

Java代码  收藏代码
  1. 1000000 次拼接,StringBuffer所花时间为: 203   
  2. *************************************  
  3. 1000000 次拼接,StringBuilder所花时间为: 79   
  4. *************************************  
  5. 10000 次拼接,String所花时间为: 640   

 

显然,StringBuilder的性能最好,String的性能最差,而且差 很多;不过StringBuffer的线程安全性很好,性能也比较接近StringBuilder,所以我推荐的选择使用顺序 为:StringBuffer>StringBuilder>String;

 

四、java传参


下面我们我看一段代码,不过有点长,请大家有点耐心哦~
代码四:

 

Java代码  收藏代码
  1. public   class  VariableTest {  
  2. public   static   void  main(String[] args) {  
  3. VariableTest t = new  VariableTest();  
  4.         t.test();  
  5.     }  
  6.   
  7.     class  Point {  
  8.         int  x;  
  9.         String y;  
  10.         StringBuffer sb;  
  11.                 public  Point( int  x, String y, StringBuffer sb) {  
  12.             this .x = x;  
  13.             this .y = y;  
  14.             this .sb = sb;  
  15.         }  
  16.     }  
  17.   
  18.     public   void  test() {  
  19.         int  i =  1 ;  
  20.         String str = "abc" ;  
  21.         StringBuffer bs = new  StringBuffer( "abc" );  
  22.         Point p = new  Point( 1 "2" new  StringBuffer( "abc" ));  
  23.         System.out.println("***********函数调用之前****************" );  
  24.         System.out.println("i为:"  + i);  
  25.         System.out.println("str为:"  + str);  
  26.         System.out.println("bs为:"  + bs);  
  27.         System.out.println("p的x为:"  + p.x +  "         p的y为:"  + p.y  
  28.                 + "       p的sb为:"  + p.sb);  
  29.         change(i, str, bs, p);  
  30.         System.out.println("***********函数调用之后****************" );  
  31.         System.out.println("i为:"  + i);  
  32.         System.out.println("str为:"  + str);  
  33.         System.out.println("bs为:"  + bs);  
  34.         System.out.println("p的x为:"  + p.x +  "         p的y为:"  + p.y  
  35.                 + "       p的sb为:"  + p.sb);  
  36.     }  
  37.   
  38.     public   void  change( int  p1, String p2, StringBuffer p3, Point p4) {  
  39.         p1 = 2 ;  
  40.         p2 = "I have changed!" ;  
  41.         p3 = p3.append("  I have changed!" );  
  42.         p4.x = 5 ;  
  43.         p4.y = "I have changed!" ;  
  44.         p4.sb = p4.sb.append("  I have changed!" );  
  45.     }  
  46.   
  47. }  

 

输出结果为:

 

Java代码  收藏代码
  1. ***********函数调用之前****************  
  2. i为:1   
  3. str为:abc  
  4. bs为:abc  
  5. p的x为:1          p的y为: 2        p的sb为:abc  
  6. ***********函数调用之后****************  
  7. i为:1   
  8. str为:abc  
  9. bs为:abc  I have changed!  
  10. p的x为:5          p的y为:I have changed!       p的sb为:abc  I have changed!  

 

分析:
这个例子我举得有点大,不过我觉得如果把我举得这个例子的参数传递完全搞懂了,你对java的参数传递过程就比较了解了。

不过在分析之前,我想给大家java传参的一个思想:java只有值传递,没有 引用传递,也没有指针传递。对于基本数据类型,java是直接传值,其实就是将形参指向常量池中的那个值;对于类类型(比如 String,StringBuffer,自定义类类型等)是传引用(在栈中)的值,也就是堆中对应对象的地址。这个在我认为也是值传递。

 

下面我们开始分析test()方法
1、首先定义了int类型变量,int类型变量传入change()方法是简单的值传递,这个大家都知道,所以就不说了;


2、下面是String类型的变量,大家可能会想,String类型是类类型啊,当调用change方法后test方法中也应该会发生变化呀,呵呵,其实 这时你忘了String类型是不可变的,因为它存储数据的char[]是用final修饰过的。当change方法中改变了p2的值后,其实p2指向的已 经是另一块内存空间了。


3、下面是StringBuffer类型,之前已说类类型传递变量的地址,所以bs和p3指向的是同一块内存空间,当p3重新赋值时,bs也会跟着变得。

4、下面是自定义的类类型,我不想再用文字述说了,就用一个图来表示吧,我相信你现在可以自己分析了。
 

 

 

五、java对象的克隆机制(以上概念的应用)


概念引入:


我相信大家都听过java中的“克隆”这个名词,在Object类中有一个本地化clone()方法就是用来克隆对象的,其实我们自己也可以用new来克隆对象,但这样的效率会比较低。

概念名词:


浅度克隆:要克隆对象的属性如果是类类型变量,只在栈中创建一个该属性的新引用,指向源属性对象;如果是基本数据类型,还是常量池的运用,我相信你懂得。


深度克隆:对于类类型的属性,在栈中和堆中都重新开辟空间,创建一个全新的属性对象。基本数据类型和浅度克隆一样。

其实Object中的clone()方法就是一种浅度克隆,不过当我们重写该方法时一定要实现Cloneable接口,否则会报异常,代码验证如下:
代码五:

 

Java代码  收藏代码
  1. public   class  CloneTest {  
  2.   
  3.     public   static   void  main(String[] args) {  
  4.         // TODO Auto-generated method stub   
  5.         Point p1 = new  CloneTest(). new  Point( 1 "abc" new  StringBuffer( "def" )); //源对象   
  6.         Point p2=p1.clone();     //克隆对象   
  7.         System.out.println("*************源对象的值如下****************" );  
  8.         System.out.println(p1.x);  
  9.         System.out.println(p1.y);  
  10.         System.out.println(p1.sb);  
  11.         System.out.println("************修改克隆对象的值*****************" );  
  12.         p2.x=2 ;  
  13.         p2.y="ddddddd" ;  
  14.         p2.sb=p2.sb.append("dfsfdsfsd" );  
  15.         System.out.println("************修改克隆对象的值后 ,源对象的值如下*****************" );  
  16.         System.out.println(p1.x);  
  17.         System.out.println(p1.y);  
  18.         System.out.println(p1.sb);  
  19.     }  
  20.       
  21.     /**  
  22.      * 内部类,用于克隆实验  
  23.      */   
  24.     class  Point   implements  Cloneable{  
  25.         int  x;  
  26.         String y;  
  27.         StringBuffer sb;  
  28.         //构造方法   
  29.         public  Point( int  x, String y, StringBuffer sb) {  
  30.             this .x = x;  
  31.             this .y = y;  
  32.             this .sb = sb;  
  33.         }  
  34.           
  35.         /**  
  36.          * 重写Object类的clone方法,不过默认情况下只能浅克隆,不过我们可以给类类型的变量  
  37.          * 重新new一块空间实现深度克隆,String类型就不用了哦~ ,呵呵,如果你现在还不知道  
  38.          * 为什么,那就把博客再看一遍吧,我充分相信你会懂得,这里我不想再赘述了,总之要知道,String  
  39.          * 类型和其他的类类型总是有一些区别,看到现在我希望你可以总结出一些  
  40.          */        
  41.                  public  Point clone(){  
  42.             Point o=null ;  
  43.             try  {  
  44.                 o = (Point)super .clone();  
  45.                 //o.sb=new StringBuffer();       //实现深度克隆   
  46.             } catch  (CloneNotSupportedException e) {  
  47.                 // TODO Auto-generated catch block   
  48.                 e.printStackTrace();  
  49.             }           return  o;  
  50.         }  
  51. }  
  52. }  

 

这时的运行结果如下,很显然是浅克隆。

 

Java代码  收藏代码
  1. *************源对象的值如下****************  
  2. 1   
  3. abc  
  4. def  
  5. ************修改克隆对象的值*****************  
  6. ************修改克隆对象的值后 ,源对象的值如下*****************  
  7. 1   
  8. abc  
  9. defdfsfdsfsd  

 

当我们把clone()方法中的注释语句“//o.sb=new StringBuffer();    ”启用后,这就是深度克隆了哦,运行结果如下:

 

Java代码  收藏代码
  1. *************源对象的值如下****************  
  2. 1   
  3. abc  
  4. def  
  5. ************修改克隆对象的值*****************  
  6. ************修改克隆对象的值后 ,源对象的值如下*****************  
  7. 1   
  8. abc  
  9. def  

 

上面实现深度克隆的方法是基于Object的clone()方法的,其实我们也 可以采用序列化的方式来实现深度克隆的,这样就不用重写clone()方法了,我们给Point类添加一个deepClone方法,不过一定要让 Point类实现Serializeble接口哦~,deepClone方法如下:

 

Java代码  收藏代码
  1.                /**  
  2.  * 采用序列化的方式实现深度克隆     
  3.  */   
  4. public  Point deepClone()  throws  IOException, ClassNotFoundException {  
  5. //将对象写入流中   
  6. ByteArrayOutputStream bs=   new  ByteArrayOutputStream();  
  7. ObjectOutputStream os = new  ObjectOutputStream(bs);  
  8. os.writeObject(this );  
  9.               //从流中读取对象   
  10. ByteArrayInputStream is=   new   ByteArrayInputStream(bs.toByteArray());  
  11. ObjectInputStream ois=new  ObjectInputStream(is);  
  12. return  (Point) ois.readObject();  
  13. }  

 

呵呵,通过这些实验,我想你对java的克隆机制还是比较了解了,具体的分析我也没有必要再说了。就到此为止吧•••

13
1
分享到:
评论
12 楼 luliangy 7 小时前  
考虑过常量那个东东,不过小路学长也太厉害了!
11 楼 lflei 9 小时前  
LZ研究的很透彻,学习了
10 楼 逸情公子 10 小时前  
如果还有人对8楼的问题有疑问可以看下下面的分析图:
9 楼 miroku 12 小时前  
syx278250658 写道
java传参:
void change(Point p) {
   p = new Point();
}
void test() {
   Point p = null;
   change(p);
   sysout(p);//p is null
}



具体来说 就这形参中的P和test方法里的p不是同一个  当chang方法退出的时候。栈帧退出,所以chanag方法的一切都消失 ,但是,test方法里的p还是存在的,由此,更是证明,java是按值传递
8 楼 syx278250658 12 小时前  
java传参:
void change(Point p) {
   p = new Point();
}
void test() {
   Point p = null;
   change(p);
   sysout(p);//p is null
}
7 楼 miroku 13 小时前  
另外,就是,基本类型的变量是不会放在常量池的,如果是字面值,那么编译的时候会直接用实际值代替,对于字符串字面值,只所以放在常量池,是为了效率和减少因为重复分配而导致的内存浪费
6 楼 miroku 13 小时前  
基本类型貌似分析的不对,"=="其实,是比较2个变量的内容的,因为在java里,基 本类型和引用类型都可以用16位字节表示,当变量实际是一个对象的引用的时候,该变量存的是一个整型,这个整型是代表对象在内存中的地址,所以,基本类型 和引用类型比较的都是该变量的值,只不过,引用类型指向了一个对象,所以,错觉上是比较指向
5 楼 somaliarookie 13 小时前  
楼主的帖子名如其实
4 楼 Jonathan樊 16 小时前  
学习了~~好好研究一下~~LZ好有耐心
3 楼 凍龙熙 16 小时前  
受教了
2 楼 风子柒 20 小时前  
用ITeye写博客,还能有这么好的排版,着实佩服额
分析得这么仔细,还真是痴情额
1 楼 stormhouse 21 小时前  
列举的很细,学习了
分享到:
评论

相关推荐

    探讨Java的对象是怎么在内存中产生的?

    ### 探讨Java的对象在内存中产生的机制 #### 一、引言 Java作为一种主流的面向对象编程语言,其对象的创建与管理是程序运行的基础。本文将详细探讨Java对象在JVM中的创建过程以及其内存布局,帮助读者更深入地理解...

    java内存对象分配过程研究

    ### Java内存对象分配过程研究 #### 一、引言 Java作为一门强大的面向对象编程语言,在实际开发过程中,对象的创建及其内存管理是至关重要的环节。深入理解对象在内存中的分配过程不仅能够帮助开发者设计出更为...

    java 对象 内存 大小

    在Java编程语言中,了解对象内存大小是优化内存使用、提高程序性能的关键步骤。当我们谈论“Java对象内存大小”时,我们通常指的是一个Java对象在内存中占据的空间,包括对象头、实例字段以及可能的对齐填充。这个...

    JAVA中的面向对象与内存解析

    在Java中,对象的创建过程涉及内存分配和初始化。当我们使用`new`关键字创建对象时,首先在堆内存中为对象分配空间,然后调用构造函数初始化对象的属性。对象的引用会被存储在栈内存中,供后续使用。理解这个过程有...

    JAVA对象所占内存大小计算例子

    在Java编程语言中,了解一个对象占用的内存大小对于优化程序性能、理解内存消耗以及防止内存泄漏至关重要。本文将深入探讨如何计算Java对象所占内存,并通过提供的代码示例进行详细解析。 首先,我们需要理解Java...

    java内存机制及异常处理

    Java内存机制是Java虚拟机(JVM)的关键组成部分,它管理着程序运行时的数据存储。在Java中,内存主要分为以下几个区域: 1. **Heap(堆)**:这是Java中最主要的内存区域,用于存储所有的类实例和数组。当堆空间...

    java对象在内存中的结构

    Java对象在内存中的结构及其生命周期是Java编程中基础且关键的概念。Java的内存管理主要涉及栈(Stack)和堆(Heap)两个区域,对于理解程序的性能和内存使用至关重要。 首先,栈主要用于存储基本数据类型(如int, ...

    java 使用静态成员变量计算内存中实例化的对象数目

    在Java编程语言中,了解如何计算内存中实例化对象的数量是一项重要的技能,这对于优化程序性能、理解和管理资源分配至关重要。本篇文章将详细讲解如何利用静态成员变量来追踪和计算一个类在运行时创建的实例数量。 ...

    java内存原理.doc

    在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的...

    Java内存泄露及内存无法回收解决方案

    Java内存管理是编程中至关重要的一个环节,尤其是对于大型、长时间运行的应用来说,内存泄漏和内存无法回收可能导致系统性能下降,甚至导致系统崩溃。本文将深入探讨Java内存泄露的原理,分析内存无法回收的原因,并...

    java内存泄露、溢出检查方法和工具

    Java内存管理是开发Java应用程序时的关键环节,内存泄露和溢出问题可能导致系统性能下降,甚至导致服务崩溃。本文将深入探讨如何检测和分析Java内存泄露与溢出,并介绍一种常用的工具——Memory Analyzer(MAT)。 ...

    Java对象内存布局 - 小分析

    本篇文章将深入探讨Java对象在JVM内存中的布局,帮助我们理解JVM是如何存储和管理对象的。 首先,我们要知道JVM内存主要分为以下几个区域: 1. **堆内存(Heap)**:这是Java对象的主要存储区域,所有通过`new`...

    JAVA内存溢出问题总结

    JAVA内存溢出问题总结 JAVA 内存溢出问题是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用的内存大于虚拟机能提供的最大内存。内存溢出问题可以从容器和程序类两个方面进行排查,容器问题...

    Java内存使用系列一Java对象的内存占用Java开发J

    对齐填充可能存在于对象中,以确保对象大小对齐,这取决于JVM的实现。 Java对象的内存占用不仅取决于其字段数量和类型,还受到对象的实例化方式、类加载器和JVM的内存设置等因素的影响。例如,基本类型(如int、...

    如何解决Java内存泄漏

    ### 如何解决Java内存泄漏 #### 1. 背景 Java凭借其垃圾回收机制大大简化了内存管理,使得开发者无需手动管理内存的释放,从而提升了开发效率。然而,这种自动化管理也可能成为一把双刃剑,特别是当开发人员忽视...

    java内存对象搜索辅助工具,配合IDEA在Java应用运行时,对内存中的对象进行搜索

    Java内存对象搜索辅助工具是一款强大的开发辅助软件,它与IntelliJ IDEA集成,可以在Java应用程序运行时实时查找和分析内存中的对象。这个工具对于开发者来说是极其宝贵的,尤其是在进行复杂问题排查、性能优化或者...

    java中的栈(深层了解java虚拟机对对象的内存分布)

    ### 深层解析Java虚拟机中的栈与堆:对象的内存分布 #### 核心概念:栈与堆的本质及作用 在Java编程语言中,理解栈(stack)和堆(heap)的概念及其工作原理对于深入掌握Java虚拟机(JVM)如何管理内存至关重要。栈和堆...

    Java 对象搜索器 - java内存对象搜索辅助工具

    Java 对象搜索器 | java内存对象搜索辅助工具 肖像画家 肖像画家 肖像画家 0x01 工具简介 ############################################################# Java Object Searcher v0.01 author: c0ny1 github: ...

    java 对象在内存中的结构

    Java 对象在内存中的结构 Java 对象在内存中的结构是 Java 程序员需要了解的重要知识点之一。了解 Java 对象在内存中的结构可以帮助程序员更好地理解 Java 语言的底层实现,提高编程效率和代码质量。在 Java 中,...

Global site tag (gtag.js) - Google Analytics