java内存分配分析/栈内存、堆内存

首先学习JVM相关需要需要内存的组成。

基本内容


  • java动态创建对象,即对于new的一个实例对象。但是需要注意的是该实例对象的成员变量都存储在各自的堆区域中,其中对象方法是在堆中共享,即不是每次创建都复制一份。

  • 保存局部变量的值包括
    1. 基本数据变量
    2. 引用变量的保存,即堆对象的引用。当然也可以用来保存加载方法时的帧。
  • 寄存器
    JVM内部虚拟寄存器,存取速度非常快,程序不可控制。
  • 常量池
    • 首先明确的是:常量池存在于堆中。
    • 即JVM为每个已加载的类型开辟一块区域,包括基本类型和String类型(其中Float和Double除外),对其他类型、方法、字段的符号引用。
    • 池中的数据和数组一样通过索引访问
    • 由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。
  • 代码区
    用来存放从硬盘上读取的源程序代码。
  • 数据段
    用来存放static修饰的静态成员(在java中static的作用就是说明该变量,方法,代码块是属于类的还是属于实例的

示意图展示

内存组成.jpg

)。

个人经验之谈


总结:

  1. 分清什么是对象引用变量(引用变量)什么是对象。Class a= new Class();此时a叫对象引用变量,而不能说a是对象。引用变量在栈中,对象在堆中,操作引用变量实际上是通过引用间接操作对象。多个引用变量可以引用到同一个对象。
  2. 栈中的数据和堆中的数据销毁并不是同步的。方法一旦结束,栈中的局部变量立即销毁,但是堆中对象不一定销毁。因为可能有其他变量也指向了这个对象,直到栈中没有变量指向堆中的对象时,它才销毁,而且还不是马上销毁,要等垃圾回收扫描时才可以被销毁。****
  3. 每个方法执行的时候都会建立自己的栈区,在方法中定义的局部变量(参数,方法中定义的变量)都在栈区中存放当方法结束时这些局部变量也就结束了,但是堆内存中的对象不会随着方法的结束而销毁而是判断还有没有引用变量引用到这个对象如果有的话就是说这个对象可达所以不会轻易的被GC回收,如果这个对象没有被引用如果这时垃圾回收系统开始回收但发现这个对象没有引用的话就会调用finalize()方法来判断这个对象是否可以再次可达如果可以的不会回收但是不过不可达的话可能会被回收(不是一定会被回收这里是不一定会回收因为这里还有对象的引用类型如:强引用,软引用(softReference来实现),弱引用(WeakReference来实现)等因素有关,还要考虑其他的因素不在这里一一说明)如果可达的话还是不会回收的。
  4. 以上的栈、堆、代码段、数据段等等都是相对于应用程序而言的。每一个应用程序都对应唯一的一个JVM实例,每一个JVM实例都有自己的内存区域,互不影响,调用JVM也就是激活一个进程。并且这些内存区域是所有线程共享的。这里提到的栈和堆都是整体上的概念,这些堆栈还可以细分。
  5. 类中定义的实例成员变量在不同对象中各不相同,都有自己的存储空间(成员变量在堆中的对象中)。而类中定义的方法却是该类的所有对象共享的,只有一套,对象使用方法的时候方法才被压入栈,方法不使用则不占用内存。

以上分析只涉及了栈和堆,还有一个非常重要的内存区域:常量池,这个地方往往出现一些莫名其妙的问题。只要记住它维护了一个已加载类的常量就可以了。接下来结合一些例子说明常量池的特性。

预备知识:


基本类型和基本类型的包装类。基本类型有:byte、short、char、int、long、boolean。基本类型的包装类分别是:Byte、Short、Character、Integer、Long、Boolean。注意区分大小写。二者的区别是:基本类型体现在程序中是普通变量,基本类型的包装类是类,体现在程序中是引用变量。因此二者在内存中的存储位置不同:基本类型存储在栈中,而基本类型包装类存储在堆中。上边提到的这些包装类都****实现了常量池技术,而两种浮点数类型的包装类则没有实现。另外,String类型也实现了常量池技术。

实例:

public class test {  
    public static void main(String[] args) {      
        objPoolTest();  
    }  
    public static void objPoolTest() {  
        int i = 40;  
        int i0 = 40;  
        Integer i1 = 40;  
        Integer i2 = 40;  
        Integer i3 = 0;  
        Integer i4 = new Integer(40);  
        Integer i5 = new Integer(40);  
        Integer i6 = new Integer(0);  
        Double d1=1.0;  
        Double d2=1.0;  
          //在java中对于引用变量来说“==”就是判断这两个引用变量所引用的是不是同一个对象
        System.out.println("i==i0\t" + (i == i0));  
        System.out.println("i1==i2\t" + (i1 == i2));  
        System.out.println("i1==i2+i3\t" + (i1 == i2 + i3));  
        System.out.println("i4==i5\t" + (i4 == i5));  
        System.out.println("i4==i5+i6\t" + (i4 == i5 + i6));      
        System.out.println("d1==d2\t" + (d1==d2));   
        System.out.println();          
    }  
}  

结果:

i==i0    true  
i1==i2   true  
i1==i2+i3        true  
i4==i5   false  
i4==i5+i6        true  
d1==d2   false  

结果分析:


  1. i和i0均是普通类型(int)的变量,所以数据直接存储在栈中,而栈有一个很重要的特性:栈中的数据可以共享。当我们定义了int i = 40;,再定义int i0 = 40;这时候会自动检查栈中是否有40这个数据,如果有,i0会直接指向i的40,不会再添加一个新的40。
  2. i1和i2均是引用类型,在栈中存储指针,因为Integer是包装类。由于Integer包装类实现了常量池技术,因此i1、i2的40均是从常量池中获取的,均指向同一个地址,因此i1==12。
  3. 很明显这是一个加法运算,Java的数学运算都是在栈中进行的Java会自动对i1、i2进行拆箱操作转化成整型,因此i1在数值上等于i2+i3。
  4. i4和i5均是引用类型,在栈中存储指针,因为Integer是包装类。但是由于他们各自都是new出来的,因此不再从常量池寻找数据,而是从堆中各自new一个对象,然后各自保存指向对象的指针,所以i4和i5不相等,因为他们所存地址不同,所引用到的对象不同。
  5. 这也是一个加法运算,和3同理。
  6. d1和d2均是引用类型,在栈中存储指针,因为Double是包装类。但Double包装类没有实现常量池技术,因此Doubled1=1.0;相当于Double d1=new Double(1.0);,是从堆new一个对象,d2同理。因此d1和d2存放的指针不同,指向的对象不同,所以不相等。

小结:


  1. 以上提到的几种基本类型包装类均实现了常量池技术,但他们维护的常量仅仅是【-128至127】这个范围内的常量,如果常量值超过这个范围,就会从堆中创建对象,不再从常量池中取。比如,把上边例子改成Integer i1 = 400; Integer i2 = 400;,很明显超过了127,无法从常量池获取常量,就要从堆中new新的Integer对象,这时i1和i2就不相等了。
  2. String类型也实现了常量池技术,但是稍微有点不同。String型是先检测常量池中有没有对应字符串,如果有,则取出来;如果没有,则把当前的添加进去。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 本文将由浅入深详细介绍Java内存分配的原理,以帮助新手更轻松的学习Java。这类文章网上有很多,但大多比较零碎。...
    OSET我要编程阅读 881评论 0 1
  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,564评论 3 83
  • java笔记第一天 == 和 equals ==比较的比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量...
    jmychou阅读 1,488评论 0 3
  • 相关概念 常量池的定义常量池(constant pool):指的是在编译期被确定,并被保存在已编译的.class文...
    snoweek阅读 787评论 0 4
  • 最近身边
    依依妍阅读 144评论 0 0