深入理解栈

  1. 栈又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把 另一端称为栈底。其特性是先进后出。

  2. 栈是线程私有的,生命周期跟线程相同,当创建一个线程时,同时会创建一个栈,栈的大小和深度都是固定的。

  3. 方法参数列表中的变量,方法体中的基本数据类型的变量和引用数据类型的引用都存放在栈中,成员变量和对象本身不存放在栈中。运行时,成员函数的局部变量引用也存放在栈中。

  4. 栈的变量随着变量作用域的结束而释放,不需要jvm垃圾回收机制回收。

  5. 栈不是全局共享的,每个线程创建一个栈,该线程只能访问其对应的栈数据

  6. 栈内存的大小是在编译期就确定了的。

栈帧:

  1. **一个栈中可以有多个栈帧,栈帧随着方法的调用而创建,随着方法的结束而消亡。**该栈帧中存储该方法中的变量,原则上各个栈帧之间的数据是不能共享的,但是在方法间调用时,jvm会将一方法的返回值赋值给调用它的栈帧中。每一个方法调用,就是一个压栈的过程,每个方法的结束就是一个弹栈的过程。压栈都将会将该栈帧置于栈顶,每个栈不会同时操作多个栈帧,只会操作栈顶,当栈顶操作结束时,会将该栈帧弹出,同时会释放该栈帧内存,其下一个栈帧将变为栈顶。栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。
  2. 栈帧的组成部分分为局部变量表、操作数栈、动态连接、方法返回地址。

栈的优点:

  1. 栈帧内存数据共享:栈帧之间数据不能共享,但是同一个栈帧内的数据是可以共享的,这样设计是为了减小内存消耗,例如:int a = 1, int b= 1时,**前面定义了a=1,a和1都在栈内存内,如果再定义一个b=1,此时将b放入栈内存,然后查找栈内存中是否有1,如果有则b指向1。**如果再给b赋值2,则在栈内存中查找是否有2,如果没有就在栈内存中放一个2,然后b指向2。也就是如果常量在栈内存中,就将变量指向该常量,如果没有就在该栈内存增加一个该常量,并将变量指向该常量。

  2. 存取速度比堆要快,仅次于寄存器。速度快之一是栈在编译器就申请好了内存空间,所以在运行时不需要申请内存大小,节约了时间,其二是栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。其三是访问时间,访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次。

栈的缺点:

  • 存在栈的数据大小和生存期必须是确定的,缺乏灵活性。当栈在运行执行程序时,发现栈内存不够,不会动态的去申请内存,以至于导致程序报错,所以灵活性较差。

1、栈里面存放什么

栈:8大基本类型 + 对象的引用 + 实例的方法

2、栈运行原理

栈帧

栈满了:StackOverflowError

3、栈堆方法区的交互关系

深入理解堆

Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存**逻辑上(注意是逻辑上)**分为三部分:

新生区、养老区、永久区。

1、堆里面存放什么

类加载器读取了类文件后,一般会把什么东西放在堆中? 类,方法,常量,变量,保存我们所有引用类型的真实对象

堆内存中逻辑上还要细分为三个区域:

  • 新生区(伊甸园区)young/new
  • 养老区 old
  • 永久区(Java 8 此区域改叫元空间,后面介绍) perm

堆内存中物理上只有新生区养老区,其内存比例为1:2。

GC垃圾回收,主要在伊甸园区养老区

假设内存满了,会触发OOM(error),堆内存不够!java.lang.OutOfMemoryError: Java heap space

在JDK8以后,永久存储区改了个名字叫:元空间

2、新生区

  • 新生区:类诞生、成长、死亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命,新生区又分为两个部分:

    • 伊甸园区,所有对象都是在伊甸园区new出来的。
    • 幸存者区有两个分为:0 = from区 和 1 = to 区。
    • Eden区跟幸存者两个区默认出厂内存比例:8:1:1

新生区养老区的操作:

当Eden的空间用完时,程序又需要创建对象, JVM的垃圾回收器将对Eden区进行垃圾回收(Minor GC),将Eden区中不再被其他对象所引用的对象进行销毁。

然后将Eden区中剩余的对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区,如果1区也满了,就移动到养老区。若养老区也满了,这时候除非MajorGC(FullGC),进行养老区的内存清理。若养老区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM(OutOfMemoryError)。

MinorGC的过程(复制—>清空—>互换)

  1. 复制

    当Eden区满的时候会触发第一次GC,把还活着的对象拷贝到幸存者From区,当Eden区再次触发GC的时候会扫描Eden区和From区,对这两个区域进行垃圾回收, 经过这次回收后还存活的对象,则直接复制到To区域(如果有对象的年龄达到了老年代区,则复制到老年代区),同时把这些对象的年龄+1。

  2. 清空Eden,From区

    上述操作完成后,清空Eden和From中的对象。

  3. To和From的互换

    最后,To和From互换,原To区成为下一次GC时的From区,部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数,MaxTenuringThreshold决定,默认为15,最大也是15因为只留了4个字节),最终如果还是存活,就存入老年代。

​ from区和to区,他们的位置和名分,不是固定的,每次GC后会交换,GC之后有交换,谁空谁是To,To区不参与垃圾回收,只是暂存(因为下次GC时又成了From),通过To和From交换次数来确定此对象是否为长期存活对象,是的话进入养老区。

真理:进过研究,99%的对象都是临时对象!

3、养老区

上文提到的经过To和From交换15次(默认为15,可调)的对象进入养老区。养老区满了会触发FullGC,若还是满的则抛出OOM

4、永久区

上文中我们提到方法区是一规范,永久区则是实现了这个规范。

这个区域是常驻内存的。用来存放JDK自身携带的Class对象,Interface元数据,存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收!当关闭VM虚拟机就会释放这个区域的内存。

一个启动类加载了大量的第三方jar包;Tomcat部署了太多的应用;大量动态生成的反射类等 不断的被加载,直到内存满,就会出现OOM。

  • jdk1.6 之前:永久代,常量池是在方法区中;
  • jdk1.7 :永久代,但是慢慢退化了,去永久代,常量池在堆中
  • jdk1.8 之后:无永久代,常量池在元空间

但是,元空间:逻辑上存在,物理上不存在于堆中

package com.draco.heapOverflow;/*** 元空间逻辑上存在,物理上不存在*/
public class SanQu {public static void main(String[] args) {// 返回jvm试图使用的最大内存long max = Runtime.getRuntime().maxMemory();// 返回jvm的初始化内存long total = Runtime.getRuntime().totalMemory();System.out.println("max="+max+"字节\t"+(max/(1024*1024))+"MB");System.out.println("total="+total+"字节\t"+(total/(1024*1024))+"MB");//默认情况下,试图分配的最大内存是电脑内存的1/4,而初始化的内存是1/64// -Xms1024m -Xmx1024m -XX:+PrintGCDetails}
}

运行结果:

当修改了VM选项后:-Xms1024m -Xmx1024m -XX:+PrintGCDetails,输出结果:

让我们来算一笔账,

新生区:305664k;养老区:699392k

加在一起:1,005,056k,除以1024后 = 981.5MB,等于jvm试图分配的最大内存,所以说元空间逻辑上存在,物理上不存在。

5、出现OOM

  1. 尝试扩大堆内存去查看内存结果

    -Xms1024m -Xmx1024m -XX:+PrintGCDetails

  2. 若不行,分析内存,看一下是哪个地方出现了问题(专业工具)

    • 能够看到代码第几行出错:内存快照分析工具,MAT(eclipse),Jprofiler
    • Dubug,一行行分析代码!(不现实)

MAT,Jprofiler作用:

  • 分析Dump内存文件,快速定位内存泄漏
  • 获得堆中的数据
  • 获得大的对象

运行出现堆溢出:

package com.draco.heapOverflow;import java.util.ArrayList;public class JprofilerTest {byte[] array = new byte[1*1024*1024];public static void main(String[] args) {ArrayList<JprofilerTest> list = new ArrayList<>();int count = 0;try {while(true){list.add(new JprofilerTest());count++;}}catch (Exception e){System.out.println("count="+count);e.printStackTrace();}}
}

设置VM options -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError后,再次运行,控制台输出:

找到HPROF快照

双击打开

修改代码,把try catch中的Exception改成Error后运行:

6、VM options参数

-Xms 设置初始化内存分配大小,默认1/64

-Xmx 设置最大分配内存,默认1/4

-XX:+PrintGCDetails 打印GC垃圾回收信息

-XX:+HeapDumpOnOutOfMemoryError 生成oomDump文件

-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

深入理解JVM中的栈和堆相关推荐

  1. Java虚拟机中的栈和堆

    Java虚拟机中的栈和堆 简单的说: Java把内存划分成两种:一种是栈内存,一种是堆内存. 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配. 当在一段代码块定义一个变量时,J ...

  2. JVM中的栈和局部变量

    JVM中的栈和局部变量 Java开发中,每当我们在程序中使用new生成一个对象,对象的引用存放在栈里,而对象是存放在堆里的.可以看出栈在Java核心的重要位置.今天我们就继续深入Java核心这个系列, ...

  3. 深入Java核心:JVM中的栈和局部变量

    2019独角兽企业重金招聘Python工程师标准>>> Java开发中,每当我们在程序中使用new生成一个对象,对象的引用存放在栈里,而对象是存放在堆里的.可以看出栈在Java核心的 ...

  4. java什么是栈和堆_JAVA中的栈和堆

    JAVA在程序运行时,在内存中划分5片空间进行数据的存储.分别是:1:寄存器.2:本地方法区.3:方法区.4:栈.5:堆. 基本,栈stack和堆heap这两个概念很重要,不了解清楚,后面就不用学了. ...

  5. java 栈的变量_深入Java核心:JVM中的栈和局部变量

    操作数栈和局部变量区一样,操作数栈也被组织成一个以字长为单位的数组.但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的.可把操作数栈理解为存储计算时,临时数据的存储区域.下面我们通过 ...

  6. 浅谈Java中的栈和堆

    人们常说堆栈堆栈,堆和栈是内存中两处不一样的地方,什么样的数据存在栈,又是什么样的数据存在堆中? 这里浅谈Java中的栈和堆 首先,将结论写在前面,后面再用例子加以验证. Java的栈中存储以下类型数 ...

  7. java栈与堆_JAVA中的栈和堆

    JAVA在程序运行时,在内存中划分5片空间进行数据的存储.分别是:1:寄存器.2:本地方法区.3:方法区.4:栈.5:堆. 基本,栈stack和堆heap这两个概念很重要,不了解清楚,后面就不用学了. ...

  8. java的栈区 堆区存放什么_简单整理java中的栈内存, 堆内存是什么?

    上一篇文章简单整理了栈(stack), 堆(heap), 队列(queue)的结构 这一篇继续整理java中的 占内存,堆内存. Java把内存划分成两种:一种是栈内存,一种是堆内存. 这里需要解释一 ...

  9. 深入理解 JVM 中的 returnAddress

    参考文档:jvms12 数据类型 在 JVM 中,数据分为两大类:primitive types (原生类型)和 reference types(引用类型). 引用类型,让 JVM 能更好的支持于面向 ...

最新文章

  1. 感光电路板制作(转收藏)
  2. __builtin__与__builtins__的区别与关系
  3. 【项目管理】敏捷原则
  4. Linux系统之更改默认块大小
  5. linux uwsgi 非root,nginx – 只能用root运行uwsgi
  6. python开发最受欢迎的十款工具
  7. mysql命令 show_mysql show 相关命令
  8. 火狐浏览器历史版本下载地址
  9. 串口连接交换机,进行交互
  10. 数据库原理与应用教程(何玉洁 第四版)第七章课后题
  11. 双球坐标系_【天文】教你认识三大天球坐标系!(上)
  12. access 英语什么意思_access是什么意思_access怎么读_access翻译_用法_发音_词组_同反义词_入口_出口-新东方在线英语词典...
  13. 赛格威机器人待遇_赛格威配送机器人:年内做到10天无故障 3年10万台
  14. lol计算机内存,你每打一把LOL,腾讯就会多往你电脑里塞600M垃圾
  15. 我的编程之路(三) 蜗居
  16. 测试上线邮件书写规范
  17. 杠杆炒股平台是不是实盘?
  18. 【Proteus仿真】74HC165功能验证
  19. RSA加密超长数据 前端js 作为参考
  20. windows 连接 人大金仓 php版本 已解决

热门文章

  1. 传统机器学习分类模型预测股价涨跌
  2. 一 HTML概述,网站首页案例及其知识点
  3. 嵌入式linux汽车仪表,基于ARM和嵌入式Linux的汽车仪表盘研制
  4. 【实践】信息流推荐算法实践 深入
  5. 调查:秋色园QBlog 博客开源不开源,您的建议是?
  6. MyEclipse个性设置
  7. 数据赋能 兴业惠民 | 山东大赛潍坊赛场决赛战火将起
  8. task6 .OR、IF以及whilemd
  9. python的函数式编程实例_函数式编程例子
  10. 精细化边缘安全防护:如何防止CDN域名被恶意刷量?