Java中的内存管理和垃圾回收机制 (上)

内存管理和垃圾收集

微信搜索关注《Java学研大本营》,加入读者群,分享更多精彩

JavaTM 2 Platform, Standard Edition (J2SETM) 的优势在于执行自动内存管理,这为开发人员提供了防御,使其免受显式内存管理的复杂性和担忧。

内存管理

内存管理是识别和发现不再被任何引用指向或定位的对象,并取消分配(释放内存)此类对象使用的内存的过程。该过程可以是显式的或自动的,

显式与自动内存管理

显式内存管理是程序员的责任,这会导致我们遇到一些问题,例如悬挂引用、内存/空间泄漏等。内存管理的替代方法是自动内存管理,这主要由现代 OOP 语言实现。

自动内存管理由称为垃圾收集器的程序完成。

内存模型

Java 内存模型指定 JVM 如何与计算机内存 RAM 一起工作。JVM 中包含的 Java 内存模型将 RAM 分为两个不同的区域。

  • Stack

  • Heap

Stack

堆栈内存用于线程的执行。

在 Java 中,每个线程都在堆栈中占据自己独立的空间,该线程堆栈还包含有关将被线程调用的方法的信息。

它还存储有关这些方法中存在的本地字段的信息。

每当一个新线程生成时,它将在堆栈上获得一个完全独立的部分,并且该堆栈存储有关方法和局部变量的信息,这些信息对其他线程的堆栈完全隐藏

所有值类型(原始数据类型)将完全(引用和实际值)保留在堆栈中——线程占用的空间,并且它们不能从其他线程堆栈访问

例如:

线程1-方法M1()=>局部变量1,局部变量2

线程2-方法M2()=>局部变量1,局部变量2

模型看起来像:

如您所见,线程在堆栈中有自己不同的空间,但它们共享堆数据。堆栈空间小于堆,这就是它们包含短期值的原因——本地值和引用堆中实际值的本地对象的引用。

堆栈总是被称为 LIFO 结构,每当我们创建一个新方法时,它都会在其调用线程的部分中相应地占用新的空间块。

每个方法调用占用空间,当方法完成其任务时,空间将被释放用于下一个方法调用。当堆栈耗尽时,它将从 VirtualMachineError 中抛出 StackOverFlowError

让我们举个例子,

public void test () {test () ;}

现在这个递归示例将永远不会中断,它将始终调用测试方法,而测试方法将永远不会结束,因此堆栈空间将结束。

Heap

Java 运行时使用的 Java 堆空间,用于为 Java 对象和 JRE 类库分配内存。每当我们使用实例化创建一个新对象时,它都会占用堆中的空间。垃圾收集器在堆上运行,通过删除没有引用的对象来释放空间。单个 JVM 进程只有一个堆空间,以确保每个线程都有共同的共享堆。

堆进一步分为不同的统治

  • 年轻一代

  • 老一辈

  • 非堆内存

新生代 & Minor GC

年轻代是所有新对象创建的地方,每当你实例化一个新对象时,它都会存储在年轻代中。年轻代有两个不同的区域,Eden Space 和 Survival Space。当你的 Eden 空间已满时,垃圾收集器将调用以删除任何未使用的对象,即垃圾收集称为 Minor GC。

年轻一代有两个生存区域。

  • 到生存空间

  • 来自生存空间

老年代 & Major GC

Old generation 也称为 tenured space,当对象在 certial garbage collection 迭代后仍然存活时,然后对象移动到 tenured 空间,当 tenured 已满时,然后从 Eden 运行到 tenured 空间的主要 GC 运行以删除所有未使用的对象

永久空间

Permgen 空间用于存储 JVM 在整个过程中所需的那些信息,如类元数据、静态字段和方法、运行时常量以及方法和构造函数的代码。Permgen就像堆一样被所有线程共享

Permgen 有以下区域,

  • 方法区

  • 内存池

  • 运行时常量池 方法区存储类元数据、方法和构造函数代码,其中内存池负责存储不可变的东西,例如字符串池和运行时常量池是方法区的一部分,用于存储类的静态和运行时变量

对象分配

对象创建或分配需要堆中的一些内存。对象创建本身是一个需要一些时间、处理和计算的过程。

注意:虚拟机使用两种技术进行对象分配,bump-the-pointer 技术和 TLAB(Thread-local allocation buffer)

Bump-the-pointer

对于快速分配VM,使用bump-the-pointer技术,在这种技术中,当添加对象时,指针会更新以保持跟踪记录,当请求新对象时,需要做的就是检查新对象是否适合指针的剩余空间如果是,对象将被添加到顶部并且指针将相应地更新为新的对象地址。这是一种快速的对象分配算法,但也有一些缺点。指针会一直不断更新自己,记录最新添加的对象,用于计算新对象可以容纳在年轻代的剩余大小。

现在让我们通过下面的图片来看一下。

现在,当第一个对象被分配时,指针会保留它的记录,所以当新对象要分配时,它的大小将与指针和剩余内存空间进行比较,如果它适合的话,它将被存储,并且指针会用那个新对象更新,所以每当新的对象只启动最后添加的对象需要检查这就是为什么它提供非常快速的分配。这种技术被称为 bump-the-pointer 技术。

这种技术的缺点是,如果我们使用多线程将对象存储在 Eden 中,它将不可避免地为线程安全创建一个锁,从而降低分配速度。

TLAB(线程本地分配缓冲区)

为了解决多线程和避免全局锁,JVM(HotSpot JVM)采用了一种称为TLAB的技术,其中每个线程都有自己的缓冲区,只有一个线程可以分配,因为每个线程都使用Bump-the在自己的缓冲区中分配对象-指针技术,速度快,线程安全。

只有在 TLAB 已满并且线程需要获取新的 TLAB 的情况下才需要同步。上述技术被 JVM 不同实例使用,如 sun JDK、IBM JVM、hotspot JVM、OpenJDK 等。这些技术仅用于快速和线程安全的对象分配。

如果TLAB满了,那么Eden空间也有一个公共区域,对象从TLAB移到公共区域,显然需要同步。如果公共区域也已满,则处理次要 GC。

推荐书单

《项目驱动零起点学Java》

《项目驱动零起点学Java》共分 13 章,围绕 6 个项目和 258 个代码示例,分别介绍了走进Java 的世界、变量与数据类型、运算符、流程控制、方法、数组、面向对象、异常、常用类、集合、I/O流、多线程、网络编程相关内容。《项目驱动零起点学Java》总结了马士兵老师从事Java培训十余年来经受了市场检验的教研成果,通过6 个项目以及每章的示例和习题,可以帮助读者快速掌握Java 编程的语法以及算法实现。扫描每章提供的二维码可观看相应章节内容的视频讲解。

《项目驱动零起点学Java》贯穿6个完整项目,经过作者多年教学经验提炼而得,项目从小到大、从短到长,可以让读者在练习项目的过程中,快速掌握一系列知识点。

马士兵,马士兵教育创始人,毕业于清华大学,著名IT讲师,所讲课程广受欢迎,学生遍布全球大厂,擅长用简单的语言讲授复杂的问题,擅长项目驱动知识的综合学习。马士兵教育获得在线教育“名课堂”奖、“最受欢迎机构”奖。

赵珊珊,从事多年一线开发,曾为国税、地税税务系统工作。拥有7年一线教学经验,多年线上、线下教育的积累沉淀,培养学员数万名,讲解细致,脉络清晰。

《项目驱动零起点学Java》(马士兵,赵珊珊)【摘要 书评 试读】- 京东图书京东JD.COM图书频道为您提供《项目驱动零起点学Java》在线选购,本书作者:,出版社:清华大学出版社。买图书,到京东。网购图书,享受最低优惠折扣!icon-default.png?t=N5K3https://item.jd.com/13607758.html

精彩回顾

数据架构演进史(上)

数据架构演进史(下)

如何选择适合的后端框架

微信搜索关注《Java学研大本营》

访问【IT今日热榜】,发现每日技术热点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值