zoukankan      html  css  js  c++  java
  • 了解栈内存堆内存

    由于 java 有垃圾回收机制,所以往往不太会去关注栈堆的内存分配问题,直至OOM

    一、了解栈堆概念

    1、堆【存储对象创建实例】

    1. 程序开始运行时,JVM从OS获取一些内存,部分是堆内存。堆内存通常在存储地址的底层,向上排列。
    2. 堆是一个"运行时"数据区,类实例化的对象就是从堆上去分配空间的;
    3. 在堆上分配空间是通过"new"等指令建立的,堆是动态分配的内存大小,生存期也不必事先告诉编译器;
    4. 与C++不同的是,Java自动管理堆和栈,垃圾回收器可以自动回收不再使用的堆内存;
    5. 缺点是,由于要在运行时动态分配内存,所以内存的存取速度较慢。

    2、栈【存放基本类型和引用类型】

    1. 先进后出的数据结构,通常用于保存方法中的参数,局部变量;
    2. 在java中,所有基本类型(short,int, long, byte, float, double,boolean, char)和引用类型的变量都在栈中存储;
    3. 栈中数据的生存空间一般在当前scopes内(由{…}括起来的区域;
    4. 栈的存取速度比堆要快,仅次于直接位于CPU中的寄存器;
    5. 栈中的数据可以共享,多个引用可以指向同一个地址;
    6. 缺点是,栈的数据大小与生存期必须是确定的,缺乏灵活性。

    3、总结

    1. Java堆内存是操作系统分配给JVM的内存的一部分。
    2. 当我们创建对象时,它们存储在Java堆内存中。
    3. 你可以通过用JVM的命令行选项 -Xms, -Xmx, -Xmn来调整Java堆空间的大小。
    4. 栈中存放的基本类型数据:short,int, long, byte, float, double,boolean, char
    5. 只要采用 new 的形式创建对象,那么就存储在 java 堆内存中。

    二、栈中的数据存储

    1、基本类型数据存储

    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 true

    2、包装类数据存储

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

    以String为例

    String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用String str = "abc";的形式来创建。
    前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。

    那为什么在String str = "abc";中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。

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

    • a、先定义一个名为str的对String类的对象引用变量:String str;
    • b、在栈中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的String类的对象O,并将O的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象O。如果已经有了值为"abc"的地址,则查找对象O,并返回O的地址。
    • c、将str指向对象O的地址。
       值得注意的是,通常String类中字符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用(即:String str = "abc";既有栈存储,又有堆存储)。

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

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

    只有在两个引用都指向了同一个对象时才返回真值。str1与str2是否都指向了同一个对象)

    结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。

    3、总结

    (1). 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。

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

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

    (3). 由于String类的immutable性质(因为包装类的值不可修改),当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

    4、实例

    String s = new String("abc") 创建了几个对象?

    首先我们要明白两个概念,引用变量和对象,对象一般通过new在堆中创建,s只是一个引用变量。

    答案2个。

    5、知识点

    我们可以用==判断一下两个引用变量是否指向了一个地址即一个对象

  • 相关阅读:
    IsPostBack
    判断客户端.net版本
    js 汉字转换成拼音 转载
    观察者模式
    常用的js阻止冒泡的方法
    jquery中事件的绑定
    uclinux编译 skyeye运行
    dotNet学习之路 Struct与Class异同点
    dotNet学习之路 Delegate内部原理
    设计模式之旅(策略模式) 十号刚发工资的博友们,赶紧跟我一起算算你们的老板有没有给你少发工资。。。
  • 原文地址:https://www.cnblogs.com/niceyoo/p/11084391.html
Copyright © 2011-2022 走看看