深度理解JAVA中的栈、堆、对象、方法区、类和他们之间的关系

简介: 1.方法:当一个方法执行时,该方法都会建立自己的内存栈,在该方法内定义的变量将会逐个放入内存栈中,随着方法执行结束,该方法的内存栈也将自然销毁.因此,所有在方法中定义的局部变量都是放在栈内存中的。

深度理解JAVA中的栈、堆、对象、方法区、类和他们之间的关系

最近突然灵光乍现,想总结一下Java中的栈、堆、对象、方法、类的定义和他们之间的关系,在网上看了很多,同时加上自己的一些简单理解,如有理解不对的地方请大家及时提醒,我及时更正。


方法

1.方法:当一个方法执行时,该方法都会建立自己的内存栈,在该方法内定义的变量将会逐个放入内存栈中,随着方法执行结束,该方法的内存栈也将自然销毁.因此,所有在方法中定义的局部变量都是放在栈内存中的。


对象

2.对象:创建一个对象时,该对象保存到堆内存(运行时数据区)中,以便反复使用,堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用,则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在合适的时候回收它。


这里引用一下别人的图,我看介绍挺详细的,送给大家。

20200613100822587.png

堆:主要就是用来存储对象和数组信息,所存储的对象(实例),能被所有线程共享,同时也是gc(垃圾回收机制)重点回收的一个区域模块。每个对象都包含一个与之对应的class的信息(class信息存放在方法区)。jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。

堆内存: 存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。


每个线程都会有一个独立的栈,栈上边基本类型变量(int,double,long…)的名称,值,类型这类信息都会存储在栈上边,但是对于栈上边的引用类型变量而言,栈上边会存储相应的引用对象在堆的地址。

栈内存: 栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。


在这里我也是引用了一篇大佬的例子,然后进行了一个对象的例子分析,如有不对还请大家多多指正。


比如 Person p = new Person();在内存中是这样被定义的:

1.主函数先进栈,在栈中定义一个变量p,接下来为p赋值,但是右边不是一个具体值,是一个实体。实体创建在堆里,在堆里首先通过new关键字开辟一个空间,内存在存储数据的时候都是通过地址来体现的,地址是一块连续的二进制,然后给这个实体分配一个内存地址。实体在堆内存中产生之后每一个空间都会进行默认的初始化(这是堆内存的特点,未初始化的数据是不能用的,但在堆里是可以用的,因为初始化过了,但是在栈里没有),不同的类型初始化的值不一样。所以堆和栈里就创建了变量和实体。


我们刚刚说过给堆分配了一个地址,把堆的地址赋给p,p就通过地址指向了新建对象。所以p想操纵实体时,就通过地址,而不是直接把实体都赋给它。这种我们不再叫他基本数据类型,而叫引用数据类型。称为p引用了堆内存当中的实体。(可以理解为c或c++的指针,Java成长自c++和c++很像,优化了c++)。

image.jpeg

当一个实体,没有引用数据类型指向的时候,它在堆内存中不会被释放,而被当做一个垃圾,在不定时的时间内自动回收,因为Java有一个自动回收机制,(而c++没有,需要程序员手动回收,如果不回收就越堆越多,直到撑满内存溢出,所以Java在内存管理上优于c++)。自动回收机制(程序)自动监测堆里是否有垃圾,如果有,就会自动的做垃圾回收的动作,但是什么时候收不一定。

         所以堆与栈的区别很明显:
        1.栈内存存储的是局部变量而堆内存存储的是实体;
        2.栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
        3.栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

原文链接如下,讲的会更加清晰些。

https://blog.csdn.net/pt666/article/details/70876410

image.jpeg

方法区:

关于这个概念也可以称之为静态区,静态区和堆很相似,里面存放的信息也是线程共享的,它包含的信息如下图所示:

20200613105538390.png

方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。


方法区又被称为静态区,是程序中永远唯一的元素存储区域。和堆一样,是各个线程共享的内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。


Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样 不需要连续的内存和可以选择固定大小或者可扩展之外,还可以选择不实现垃圾回收。

这区域的内存回收目标主要是针对常量池的回收和类型的卸载,一般而言,这个区域的内存回收比较难以令人满意,尤其是类型的回收,条件相当苛刻,但是这部分区域的内存回收确实是必要的。


很多开发者更愿意把方法区称为“永久代”(Perm Gen)(Permanent Generation)「总是存放不会轻易改变的内容」。在目前已经发布的JDK 1.7 的HotSpot中,已经把原本放在永久代的字符串常量池移至堆中。


运行时常量池(Runtime Constant Pool)是方法区的一部分。


JDK 1.8 中,已经没有方法区(永久代),而是将方法区直接放在一个与堆不相连的本地内存区域(Native Memory),这个区域被叫做元空间。


原文链接:

https://blog.csdn.net/Danny_idea/article/details/81137306

相关文章
|
7月前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
938 157
|
8月前
|
安全 Java 数据建模
Java记录类:简化数据载体的新选择
Java记录类:简化数据载体的新选择
442 101
|
8月前
|
安全 Java 开发者
Java记录类:简化数据载体的新方式
Java记录类:简化数据载体的新方式
353 100
|
9月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
620 143
|
7月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
296 4
|
7月前
|
IDE JavaScript Java
在Java 11中,如何处理被弃用的类或接口?
在Java 11中,如何处理被弃用的类或接口?
347 5
|
7月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
430 5
|
7月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
373 1
|
7月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
488 1
|
7月前
|
存储 Java 程序员
【Java】(6)全方面带你了解Java里的日期与时间内容,介绍 Calendar、GregorianCalendar、Date类
java.util 包提供了 Date 类来封装当前的日期和时间。Date 类提供两个构造函数来实例化 Date 对象。第一个构造函数使用当前日期和时间来初始化对象。Date( )第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。
307 0