Java内存分配---堆与栈

堆和栈的概念接触已久,也很容易让人似懂非懂。本文阐述它们的区别和作用。配合一个小例子,加深对其理解。

堆内存

堆内存是在Java程序运行时分配的,它用来存放对象,对象也总是在堆中。GC的作用域也是在堆内存上,它回收那些空引用对象。堆上的对象可以被程序全局应用到。

栈内存

栈内存是被执行线程所用的,它用来存放引用,这些引用指向堆内存上的对象。栈内存的分配依赖方法调用,当一个方法被调用到,此时一块内存区域就被分配,它用来存放方法内部声明的一些基本数据类型(int,boolean...)和指向堆内存的引用。方法一旦执行完毕,存放的内容也就被清空了。栈还有两个显著的特点:

  • 顺序。栈遵循后进先出的规则
  • 相对于堆内存,它被分配的空间很小。

以上就介绍完了堆内存和栈内存,收工!

等等,Linus说

Talk is cheap. Show me the code

实例分析

还是瞅瞅代码吧。

package com.azhengye.test;

public class Test {
    public static void main(String[] args) {
        Person yPerson = new Person("5");
        Person oPerson = new Person("100");

        swapAge(yPerson, oPerson);//能成功交换年龄吗?
        System.out.println("yPerson.age=" + yPerson.getAge());
        System.out.println("oPerson.age=" + oPerson.getAge());

        changeAge(oPerson);//更改后的年龄是多少了?
        System.out.println("oPerson.age=" + oPerson.getAge());
    }

    private static void swapAge(Person p1, Person p2) {
        Person temp = p1;
        p1 = p2;
        p2 = temp;
    }

    private static void changeAge(Person person) {
        person.setAge("5");
        person = new Person("30");
        person.setAge("40");
    }

    static class Person {
        private String age;

        public Person(String age) {
            this.age = age;
        }

        public String getAge() {
            return age;
        }

        public void setAge(String age) {
            this.age = age;
        }
    }
}

======我是答案分割线,先别往下看,自己想下结果======
<br />
<br />
<br />
<br />
<br />
答案揭晓,我们看看运行结果。


运行结果

我们看到交换年龄没有成功,而更改年龄却成功了。
下面分析下这段简单代码执行时发生的故事。

1.程序执行,JVM会把Test类加载到堆内存中,程序的入口在main方法,既然是方法,JVM就会创建出一个栈,然后将main方法压入栈中。

2.main方法中创建了两个Person对象,这两个对象存放在堆内存上,JVM如何找到它们呢?这个时候就栈中创建了两个引用yPerson/oPerson分别指向堆上的Person对象。

3.接着执行swapAge方法,在swapAge方法区域,创建temp引用交换p1和p2的指向,如果我们添加上log可以看到这个时候p1和p2可以交换成功。

private static void swapAge(Person p1, Person p2) {
        System.out.println("before: in swapAge p1 is yPerson p1.age=" + p1.getAge());
        System.out.println("before: in swapAge p2 is oPerson p2.age=" + p2.getAge());
        Person temp = p1;
        p1 = p2;
        p2 = temp;
        System.out.println("after: in swapAge p1 is yPerson p1.age=" + p1.getAge());
        System.out.println("after: in swapAge p2 is oPerson p2.age=" + p2.getAge());
    }

看看结果:


添加log后swapAge执行结果

4.swapAge方法执行完毕,关键的点来了,swapAge会被弹出栈,这也就意味着我们在swapAge方法内的交换操作也结束了。它不会在影响main方法区域的引用指向。

5.swapAge弹出栈后,此时栈里只剩下main方法区域了,接着执行打印log的语句:

System.out.println("yPerson.age=" + yPerson.getAge());
System.out.println("oPerson.age=" + oPerson.getAge());

注意了这里的yPerson/oPerson都是在main方法区域分配的,swapAge方法区域已久随着出栈操作被释放掉了,因此swapAge方法的交换根本不会影响yPerson/oPerson的指向。所以呈现的结果就是交换失败。

6.程序开始执行changeAge方法,同样的会为changeAge方法分配一块区域,并将该区域压入栈中。

7.changeAge方法接收的是一个指向Person("100")的引用,person.setAge("5")执行后,这时Person("100")这个在堆上的对象改变了,其age值变成了"5"。

8.接着执行

person = new Person("30");
person.setAge("40");

person先是重新被指向了堆内存上新生成的对象Person("30"),接着对该对象age做更改,将其改为"40"。

9.changeAge方法执行完毕,同样被弹出栈,随着出栈操作,changeAge方法里创建的新对象Person("30")就没有引用指向它了,在GC运行时就可能会被回收掉。当前栈里又只剩下了main方法区域。这个时候打印oPerson的对象age值,oPerson在main方法区域的指向一直没有被修改,但在changeAge方法执行时改变了oPerson所指向对象的值,因此oPerson.age值变成了"5"。

10.最后程序执行完毕main方法区域也被出栈,栈空间被释放,堆空间的对象也没有引用指向它们,同样被释放掉。程序到此结束。

上述过程中changeAge方法执行时的内存示意图如下。


changeAge方法执行时内存示意图

Stack和Heap区别

结合以上示例,最后总结下Stack和Heap的区别。

  • 堆内存可以被整个程序访问到,而栈内存在执行时分配,只能被执行线程访问到。
  • 对象在堆内存上分配,指向对象的引用在栈内存上,同时基础数据类型也是存放在栈内存。
  • 栈内存分配内存块遵循后进先出原则,且所占空间小。堆内存上对象存放更复杂,有可能被分配到新生域(Young-Generation)或者年老域(Old-Generation)上。
  • 栈内存上的分配的空间生命周期很短,方法执行完毕就不释放掉。
  • 可以用-Xms 和 -Xmx等设置项配置堆内存大小。可以用-Xss配置栈内存大小。
  • 栈内存溢出会抛出ava.lang.StackOverFlowError 。堆内存溢出则会抛出java.lang.OutOfMemoryError。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • 前言 不知道大家有没有这样一种感觉,程序员的数量井喷了。可能是因为互联网火了,也可能是各家培训机构为我们拉来了大量...
    活这么大就没饱过阅读 2,718评论 6 26
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,236评论 11 349
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,649评论 0 11
  • 关注简书已经有一阵子了,一得空便打开简书聆听别人的故事。我看简书的有个习惯,首先打开七日热门,三十日热门选择自己有...
    走在撒哈拉阅读 222评论 0 2
  • 有人帮你是幸运,学会心怀欢喜与感恩;无人帮你是命运,学会坦然面对与承担。没有人该为你做什么,因为生命本是自...
    丿Geass阅读 213评论 0 0