Day7-堆,栈,方法区和GC

Tips

  • 只要类持有对外部实力对象的引用, 垃圾回收机制就不会回收该对象

JVM中

堆和栈对比

存什么

  • 栈内存 存储基本数据类型, 局部变量方法调用和形参,栈分为java方法栈和native方法栈,

    方法栈主要记录的是方法运行时的栈帧, 每执行一个方法就会添加一个栈帧 ,方法返回后, 栈被清空, 堆等待GC回收
    为单个函数分配的那部分栈空间叫做栈帧(StackFrame)
    正在使用的栈空间叫做调用栈(CallStack)
    在内存中,栈是从高地址向低地址延伸的,即栈底对应高地址,栈顶对应低地址。

    java线程是不是开两个栈存放不同的栈帧看具体JDK, 比如Oracle JDK和OpenJDK就是一个调用栈存放两种栈帧

  • 堆内存 存储Java中的全部对象,this

int a[] = new int[4];
new int[] 存放在堆, int a[] 存放在栈

Double a[] = new Double[10000000];
   Double qq = 3.1d;
   for (int i = 0; i < a.length; i++) {
       a[i] = qq.doubleValue();
   }

a[i] = qq.doubleValue;
a[i] = Double.valueOf(qq);
a[i] = new Double(qq.doubleValue);
所以此double类的值存在堆

独有/共享

  • 栈内存归属于线程, 每个线程都会有一个栈内存, 其存储的变量只能在其所属的线程中可见, 栈内存可以理解成线程的私有内存, 所以叫线程栈

  • 堆内存的对象, 对所有线程可见, 可以被所有线程访问

异常

  • 栈没有空间存储方法调用和局部变量, JVM会抛出Java.lang.StackOverFlowError, 纯java代码无法泄漏栈空间, 它完全被JVM掌控
  • 堆没有空间存储对象, JVM会抛出java.lang.OutOfMemoryError

空间大小

  • 栈内存远小于堆内存, 栈可通过jvm参数 -XSS设置, 默认随着虚拟机和操作系统改变

执行效率

  • 栈是存取效率灵活, 仅次于寄存器, 栈数据可以共享, 但栈中的数据大小和生命周期固定, 缺乏灵活性
  • 堆是自动分配内存大小, 生存期不用告诉编译器, 等gc回收, 但是因为动态分配内存, 存储效率会比较慢

方法区

  • 方法区存类信息, 静态方法, 常量, 即时编译器编译后的代码

GC(Garabage Collection)

指的是堆中数据的回收, 首先堆可以划分为新生代和老年代


新生代继续划分为 Eden 和 Survivor Space(幸存区), Survivor Space 再被划分成 From 和 To



新对象首先被创建在 Eden, (如果对象过大,如数组,则直接放入老年代). 在 GC 中, Eden 会被移入Survivor Space. 直到对象熬过一定的Minor GC的次数, 会被移到老年代, 老年代用Major GC来清理

空间占比:

  • 新生代 : 老年代 = 1:2
  • Eden : From : To = 8:1:1

分代收集

新生代使用Minor GC, 老年代使用Major GC
Minor GC 和 Major GC 统称为 Full GC
所有的Minor GC 会触发全世界暂停 STW(stop-the-world), 停止应用程序的线程, 当然对于大多数应用,停顿的延迟可以忽略不计, 真相是大部分Eden区中的对象都能被认为是垃圾,所以不会存放到Survivor Space.
现在很多的GC机制都会清理永久代(静态方法区)

  • JVM并不强制要求GC实现哪种GC算法

纯java代码无法泄漏栈空间, 它完全被JVM掌控, 但如果有其他资源依附在java对象上, 如native memory(DirectByteBuffer), file(fileInputStream), 那么当然自己关闭最合适

  • 虽然有finalizer, PhantomReference之类的让程序员向GC注册, 请求释放资源,但是GC运行时间不确定(因为是一条单独的线程), 还是自己释放的好

可达性检测

  • 引用计数: 一种在jdk1.2之前被使用的垃圾收集算法,我们需要了解其思想。其主要思想就是维护一个counter,当counter为0的时候认为对象没有引用,可以被回收。缺点是无法处理循环引用。目前iOS开发中的一个常见技术ARC(Automatic Reference Counting)也是采用类似的思路。在当前的JVM中应该是没有被使用的。

  • 根搜算法: gc root 根据引用关系来便利整个堆, 并标记, 这称之为Mark, 之后回收掉违背Mark的对象, 解决了「孤岛效应」, 这里的gc root 指的是:

    • 虚拟机栈中引用的对象(栈帧中的本地变量表)
    • 方法区中的类静态属性引用的对象
    • 方法区中的常用变量的对象
    • 本地方法栈中JNI 引用的对象

java减小GC开销 from

  • 不要显示调用System.gc()
    此函数只是建议JVM进行GC, 无法保证立马执行
  • 减小临时对象的使用
  • 对象不用时显示置为null
  • 使用StringBuilder拼接字符串
    String的扩增是新建对象, 多次 + 会多次创建新对象
  • 能用基本类型就不用对象
  • 少用静态
  • 分散对象创建和删除的时间

整理策略

  • 复制
    主要在新生代的回收上, 通过from 和 to 区的来回拷贝.对于新生成的对象, 频繁的复制可以很快找到 那些不用的对象.
  • 标记清除和标记整理
    主要在老生代的回收上, 通过根搜的标记清除或者处理掉不用的对象.
    整理的过程



    清除的过程


清除会产生碎片,对内存的利用不是很好, 但是不代表整理比清除好, 毕竟整理慢, 比如CMSGC就是使用清除而不是整理的

  • 具体的垃圾收集器
    • 新生代收集器:有Serial收集器、ParNew收集器、Parallel Scavenge收集器
    • 老生代收集器:Serial Old收集器、Parallel Old收集器、CMS收集器、G1收集器


思考一下复制和标记清除/整理的区别,为什么新生代要用复制?因为对新生代来讲,一次垃圾收集要回收掉绝大部分对象,我们通过冗余空间的办法来加速整理过程(不冗余空间的整理操作要做swap,而冗余只需要做move)。同时可以记录下每个对象的『年龄』从而优化『晋升』操作使得中年对象不被错误放到老年代。而反过来老年代偏稳定,我们哪怕是用清除,也不会产生太多的碎片,并且整理的代价也并不会太大。

作者:纳达丶无忌
链接:http://www.jianshu.com/p/c9ac99b87d56
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

寄存器

在计算机领域,寄存器是CPU内部的元件,它是有限存贮容量的高速存贮部件,可用来暂存指令、数据和地址。
寄存器分为通用寄存器和特殊寄存器。通用寄存器有 ax/bx/cx/dx/di/si,在大多数指令中可以任意选用,但也有一些规定某些指令只能用某个特定的「通用」寄存器;特殊寄存器有 bp/sp/ip 等,特殊寄存器均有特定用途。

在 Stack Frame 中,涉及到三种重要的特殊寄存器:

  • bp ( base pointer ) 寄存器
  • sp ( stack poinger ) 寄存器
  • ip ( instruction pointer ) 寄存器

需要注意的是,不同架构的CPU,寄存器名称会添加不同的前缀来表示寄存器的大小。例如对于x86架构,字母「e」用作名称前缀,表示寄存器大小为32位;对于x86_64架构,字母「r」用作名称前缀,表示寄存器大小为64位。

举例

  • 下图是linux 中一个进程的虚拟内存分布:
  • 图中0号地址在最下边,越往上内存地址越大。
    以32位地址操作系统为例,一个进程可拥有的虚拟内存地址范围为0-2^32。分为两部分,一部分留给kernel使用(kernel virtual memory),剩下的是进程本身使用, 即图中的process virtual memory。
    普通Java 程序使用的就是process virtual memory.
    上图中最顶端的一部分内存叫做user stack. 这就是题目问的 stack. 中间有个 runtime heap。就是题目中的heap. 他们的名字和数据结构里的stack 和 heap 几乎每啥关系。
    注意在上图中,stack 是向下生长的; heap是向上生长的。
    当程序进行函数调用时,每个函数都在stack上有一个 call frame。
    比如对于以下程序,
public void foo(){
  //do something...
  println("haha"); // <<<=== 在这儿设置breakpoint 1
}

public void bar(){
  foo();
}

main(){
  bar();
  println("hahaha"); // <<<=== 在这儿设置 breakpoint 2
}

当程序运行到breakponit1时,user stack 里会有三个frame
|
| main 函数的 frame-------------------
|
| bar 函数的 frame-------------------<<<=== %ebp
|
| foo 函数的 frame------------------- <<<===%esp
其中 esp 和 ebp 都是寄存器。 esp 指向stack 的顶(因为stack 向下生长,esp会向下走); ebp 指向当前frame的边界。
当程序继续执行到brekapoing 2的时候stack 大概是这样的:
|
-------------------<<<=== %ebp
|
| main 函数的 frame------------------- <<<===%esp
也就是说当一个函数执行结束后,它对应的call frame就被销毁了。(其实就是esp 和 ebp分别以东,但是内存地址中的数据只有在下一次写的时候才被覆盖。)
说了这么多,终于该说什么东西放在stack 上什么东西放在heap 上了。
最直白的解释:

public void foo(){
  int i = 0; // <= i 的值存在stack上,foo()的call frame 里。
  Object obj = new Object(); // object 对象本身存在heap 里, foo()的call frame 里存该对象的地址。
}

图片引自CMU15-213的课件
https://www.cs.cmu.edu/~213/

作者:雷博
链接:https://www.zhihu.com/question/29833675/answer/45811216
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


数据结构中

栈是先进后出的结构

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,169评论 11 349
  • JVM内存模型Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: ...
    光剑书架上的书阅读 2,477评论 2 26
  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,518评论 3 83
  • 原文阅读 前言 这段时间懈怠了,罪过! 最近看到有同事也开始用上了微信公众号写博客了,挺好的~给他们点赞,这博客我...
    码农戏码阅读 5,946评论 2 31
  • 今天下午第三节课,老师找了几名同学去把校服拿来了。 当校服被推进门的时候,我们都瞪大了眼睛,我心想:这次校服怎么是...
    席振桓阅读 262评论 0 0