ITPub博客

首页 > 应用开发 > Java > Java内存分析

Java内存分析

原创 Java 作者:称驯我 时间:2020-09-24 10:16:31 0 删除 编辑

  堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。java中的对象和数组都存放在堆中。

  栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象引用。

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

  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=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。


  下面我自己来举几个例子:

  public class TestStr {

  public static void main(String[] args) {

  // 以下两条语句创建了1个对象。"凤山"存储在字符串常量池中

  String str1 = "凤山";

  String str2 = "凤山";

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

  //以下两条语句创建了3个对象。"天峨",存储在字符串常量池中,两个new String()对象存储在堆内存中

  String str3 = new String("天峨");

  String str4 = new String("天峨");

  System.out.println(str3==str4);//false

  //以下两条语句创建了1个对象。9是存储在栈内存中

  int i = 9;

  int j = 9;

  System.out.println(i==j);//true

  //以下两条语句创建了1个对象。1对象存储在栈内存中

  Integer l = 1;//装箱

  Integer k = 1;//装箱

  System.out.println(l==k);//true

  //由于没有了装箱,以下两条语句创建了2个对象。两个1对象存储在堆内存中

  Integer l1 = new Integer(1);

  Integer k1 = new Integer(1);

  System.out.println(l1==k1);//false

  //以下两条语句创建了1个对象。i1,i2变量存储在栈内存中,两个256对象存储在堆内存中

  Integer i1 = 256;

  Integer i2 = 256;

  System.out.println(i1==i2);//false

  }

  }

  对于以上最后两个关于Integer对象的例子,在自动装箱时对于值从–128到127之间的值,使用一个实例。

  下面是对字符串常量池()的一个例子:

  String s1 = "aaa" + "bbb"; //产生了1个对象。

  由于常量的值在编译的时候就被确定了。在这里,"aaa"和"bbb"都是常量,因此变量s1 的值在编译时就可以确定。这行代码编译后的效果等同于:

  String s1 ="aaabbb";

  因此这里只创建了一个对象"aaabbb",并且它被保存在字符串池里了。

  String str1 = "凤山";

  String str2 = "凤山";

  以上两条语句只在常量池中保存了一个"凤山"对象。

  String str3 = new String("天峨");

  String str4 = new String("天峨");

  以上两条语句创建了3个对象,首先在字符串常量池中创建一个"天峨"对象,接着在堆内存中创建两个new String()对象,里面保存的是指向"天峨"对象的引用。

  另:“==“在判断对象时,其实是根据对象在堆栈中的地址判断对象是不是一样,而不是根据hashcode 值。

  在网上看见这段对Java String中的HashCode和equal的总结比较好,记录如下:

  1. hashSet中比较是否重复的依据是a.hasCode()=b.hasCode() && a.equals(b)

  2. String的hashCode依据: 以依赖于char[i]的int值以和char[i]的排列序的算法计算出的(可以去看看源码)。不依赖String的ref.

  3. String的equals依据: a==b || ( a.length=b.length && { a[i]=b[i] } )

  4. 只有用a==b时比校的才是比校的ref,也就是说这时才是比校是a与b是不是同一个对象

  5. 结论: 两个不同ref的String可能会被认为是集合中的同一个元素。


  下面分析一个代码示例:

  class BirthDate {

  private int day;

  private int month;

  private int year;

  public BirthDate(int d, int m, int y) {

  day = d;

  month = m;

  year = y;

  }

  省略get,set方法。。。

  public void display() {

  System.out.println

  (day + " - " + month + " - " + year);

  }

  }

  public class Test{

  public static void main(String args[]){

  Test test = new Test();

  int date = 9;

  BirthDate d1= new BirthDate(7,7,1970);

  BirthDate d2= new BirthDate(1,1,2000);

  test.change1(date);

  test.change2(d1);

  test.change3(d2);

  d1.display();

  d2.display();

  }

  public void change1(int i){

  i = 1234;

  }

  public void change2(BirthDate b) {

  b = new BirthDate(22,2,2004);

  }

  public void change3(BirthDate b) {

  b.setDay(22);

  }

  }


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69920910/viewspace-2723693/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: 职场感想
请登录后发表评论 登录
全部评论

注册时间:2020-09-24

  • 博文量
    10
  • 访问量
    18937