之前对JVM中堆内存和栈内存都是一直半解,今天有空就好好整理一下,用作学习笔记。

包括Java程序在内,任何程序在运行时都是要开辟内存空间的。JVM运行时在内存中开辟一片内存区域,启动时在自己的内存区域中进行更细致的划分,因为虚拟机中每一片内存处理的方式都不同,所以要单独进行管理。实际上在JVM有五种内存管理形式:

  1. 寄存器;
  2. 本地方法区;
  3. 方法区;
  4. 栈内存(stack);
  5. 堆内存(heap);

今天重点梳理一下栈内存和堆内存。

在讲解之前我们要了解一个计算机发展至今仍然无法解决的一个矛盾,就是内存的存取速度和数据大小之间的矛盾。当我们对存取速度越快,存储的数据量就越少,反之亦然。栈内存、堆内存其实就是对这种矛盾的一种妥协方式,它们有自己的优点也有自己的缺点:

  • 栈内存:存取速度要比堆内存快,仅次于CPU中的寄存器,但栈内存中数据大小和周期时固定的。
  • 堆内存:可以动态地分配内存大小,但存取速度慢。

那么栈内存、堆内存到底存储那些数据呢?

  • 栈内存中存储都是局部变量,栈中数据生存空间一般在当前scopes内(可以简单理解为{...}括起来的区域)包含所有的基本类型(int、bool、char、float、double、short、long、byte)和引用类型。
  • 堆内存存储时类的对象,即类的实体,凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性)。

另外,在举例前我们需要了解一个概念,什么是变量?变量是内存中分配的区域的名称。换句话说就是变量其实分配地址的别称,我们通过这个变量的名字就可以找到一个指向这个变量所引用的数据的内存指针。我们知道了变量的类型,也就知道了这个指针地址后面连续几个字节内存储的数据。

我们以int[] arr=new int[]{1,2,3}为例,它的内存分配如下:

从上图我们可以看到,“变量”是存在栈内存中,“变量所指向的数据”是存在堆内存中的。

下面我们举一个更为复杂的类, 来展示每一部分到底是怎么存储的:

package class1;

class Fruit{
static int x=10;
static BigWaterMelon bigWaterMelon_1=new BigWaterMelon(x); int y=20;
BigWaterMelon bigWaterMelon_2=new BigWaterMelon(y); public static void main(String[] args){
final Fruit fruit=new Fruit();
int z=30;
BigWaterMelon bigWaterMelon_3=new BigWaterMelon(z);
new Thread(){
@Override
public void run(){
int k=100;
setWeight(k);
} void setWeight(int waterMelonWeight){
fruit.bigWaterMelon_2.Weight=waterMelonWeight;
}
}.start();
} } class BigWaterMelon{
public int Weight;
public BigWaterMelon(int Weight){
this.Weight=Weight;
}
}

内存图如下:

同一种颜色代表变量和对象的引用关系

由于方法区和堆内存的数据都是线程间共享的,所以线程Main Thread,New Thread和Another Thread都可以访问方法区中的静态变量以及访问这个变量所引用的对象的实例变量。

栈内存中每个线程都有自己的虚拟机栈,每一个栈帧之间的数据就是线程独有的了,也就是说线程New Thread中setWeight方法是不能访问线程Main Thread中的局部变量bigWaterMelon_3,但是我们发现setWeight却访问了同为Main Thread局部变量的“fruit”,这是为什么呢?因为“fruit”被声明为final了。

当“fruit”被声明为final后,“fruit”会作为New Thread的构造函数的一个参数传入New Thread,也就是堆内存中Fruit$1对象中的实例变量val$fruit会引用“fruit”引用的对象,从而New Thread可以访问到Main Thread的局部变量“fruit”。

此外,栈内存有先进后出(Last in first Out)的特点,并且栈中数据生存空间一般在当前scopes内(可以简单理解为{...}括起来的区域),也就是说当方法执行结束后,方法内的局部变量在内存中就被清除了。但堆内存不会自动清除,它回不断地申请新的堆内存地址来存储新的数据。不再使用地旧数据只会当作“垃圾数据”,在C++中需要你手动清除,在JVM会自动将这些垃圾数据回收,也就是传说中地GC。

无论是栈内存还是堆内存,内存空间都是有限的。当堆内存没有可用空间时,比如递归没有跳出,JVM会抛出java.lang.StackOverFlowError;当堆内存没有空间时,比如在while循环中不断创建实例,JVM会抛出java.lang.OutOfMemoryError。

------------------------------------------------------

参考博文:https://www.cnblogs.com/pomodoro/p/11912025.html

参考博文:https://blog.csdn.net/jianghao233/article/details/82777789

Java中栈和堆讲解的更多相关文章

  1. java中栈,堆,方法区

    最近在看面试题复习javaee,所以在这里对栈,堆,方法区做一下整理 参考了https://www.cnblogs.com/hqji/p/6582365.html 1.栈 每个线程包含一个栈区,栈中只 ...

  2. java中栈、堆和方法区的关系

    另外,常量池在方法区中

  3. java中栈内存与堆内存(JVM内存模型)

    java中栈内存与堆内存(JVM内存模型) Java中堆内存和栈内存详解1 和 Java中堆内存和栈内存详解2 都粗略讲解了栈内存和堆内存的区别,以及代码中哪些变量存储在堆中.哪些存储在栈中.内存中的 ...

  4. .NET中栈和堆的比较 #1

    原文出处:http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory01122006130034PM/csharp_memory.a ...

  5. 深度剖析Java变量栈&对象堆

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  6. JAVA基础-栈与堆,static、final修饰符、内部类和Java内存分配

    Java栈与堆 堆:顺序随意 栈:后进先出(Last-in/First-Out). Java的堆是一个运行时数据区,类的对象从中分配空间.这些对象通过new.newarray.anewarray和mu ...

  7. java中垃圾回收算法讲解

      判断对象是否存活的方法: 一.引用计数算法(Reference Counting) 介绍:给对象添加一个引用计数器,每当一个地方引用它时,数据器加1:当引用失效时,计数器减1:计数器为0的即可被回 ...

  8. Java的栈和堆

    JVM的内存区域可以被分为:线程栈,堆,静态方法区(实际上还有更多功能的区域,并且这里说的是JVM的内存区域) 线程栈:      注意这个栈和数据结构中的stack有相似之处,但并不是用户态的.准确 ...

  9. c#中栈和堆的理解

    之前对栈(stack)和堆(heap)的认识很模糊,今天看了一篇关于堆栈的文章<译文---C#堆VS栈>后,仿佛有种拨开云雾见青天的感觉,当然只是一些浅显的理论的认识,这里做一些简单的记录 ...

随机推荐

  1. Java实现 蓝桥杯 算法训练 乘法次数

    乘法次数 资源限制 时间限制:1.0s 内存限制:999.4MB 问题描述 给你一个非零整数,让你求这个数的n次方,每次相乘的结果可以在后面使用,求至少需要多少次乘.如24:22=22(第一次乘),2 ...

  2. Java实现 LeetCode 65 有效数字

    65. 有效数字 验证给定的字符串是否可以解释为十进制数字. 例如: "0" => true " 0.1 " => true "abc&q ...

  3. Java实现交替字符串

    1 问题描述 输入三个字符串s1.s2和s3,判断第三个字符串s3是否由前两个字符串s1和s2交错而成且不改变s1和s2中各个字符原有的相对顺序. 2 解决方案 此处采用动态规划法,可以较大的提高时间 ...

  4. Java实现数字密码发生器

    在对银行账户等重要权限设置密码的时候,我们常常遇到这样的烦恼:如果为了好记用生日吧,容易被破解,不安全:如果设置不好记的密码,又担心自己也会忘记:如果写在纸上,担心纸张被别人发现或弄丢了- 这个程序的 ...

  5. c/c++混编

    /* head.h */#ifndef __SUM_H__ #define __SUM_H__ #ifdef __cplusplus extern "C" { #endif int ...

  6. 呀,葵花宝典![IT项目经理成长晋升记2]

    走出办公室时,老吴让王小白认真看下公司的项目管理体系和质量管理体系培训材料.公司这几年连续通过了ISO质量体系认证,通过了CMMI3,已有一套完整的组织过程体系. 因为从投标开始,到公示,还有一周时间 ...

  7. Java中的堆和栈

    Java中的堆和栈 栈内存 存放基本数据类型和引用变量 堆内存 存放运行时创建的对象 一般来说,通过new关键字创建出来的对象都放在堆内存中 由于JVM是基于堆栈的虚拟机,而每个Java程序都运行在一 ...

  8. [C#.NET 拾遗补漏]04:你必须知道的反射

    阅读本文大概需要 3 分钟. 通常,反射用于动态获取对象的类型.属性和方法等信息.今天带你玩转反射,来汇总一下反射的各种常见操作,捡漏看看有没有你不知道的. 获取类型的成员 Type 类的 GetMe ...

  9. 常见ie9兼容问题

    公司项目要求需要兼容ie9,开发过程中遇到了许多问题,在这里记录一下,希望可以帮到其他需要的小伙伴. 浏览器兼容性问题无外乎三点,css样式兼容.JavaScript兼容及h5部分标签的兼容.主要介绍 ...

  10. 实验二 Linux系统简单文件操作命令

    项目 内容 这个作业属于哪个课程 班级课程的主页链接 这个作业的要求在哪里 作业要求链接接地址 学号-姓名 17041428-朱槐健 作业学习目标 1.学习在Linux系统终端下进行命令行操作 2.掌 ...