【JVM学习】Java内存管理一

4 篇文章 0 订阅

概述

本文将结合自身对JVM的学习,对java虚拟机内存的各个区域阐述个人理解。

首先我们需要了解的是,JVM是《java虚拟机规范》定义的一种规范或标准,而我们在实际应用当中要讨论的是落地的实现,即HotSpot(常用的JVM之一)。

1.运行时数据区

我们知道java在执行一个main函数时,操作系统就会为jvm这一个应用程序分配一块内存空间用于运行咱们的程序代码。那么这块内存在运行起来后,是怎么管理和分区的呢?如下图

从数据的共享/私有属性来看,其中虚拟机栈本地方法栈程序计数器(PCR)属于线程私有区域,即这些区域每个线程都各自有一份,且相互独立、互不影响;除此之外,方法区则是属于共享区域,即每个线程都可以访问。

那么他们分别有什么作用呢?

1.1 程序计数器

程序计数器Program Counter Register(简称PCR),它是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。在多线程并发执行时,CPU在不同线程之间来回切换后,为了保证程序的有序进行,CPU需要知道上一次执行到了哪一行代码,而我们的PCR的主要作用就在于此。所有的分支、循环、跳转、异常处理等基础功能都需要依赖它来完成。

如果线程执行的是常规java方法,那么它记录的是正在执行的虚拟机字节码指令的地址;若是native方法,则记录为空(Undefined)。
同时,此内存区域是唯一一个在JVM规范当中没有规定任何OOM的区域。

1.2 虚拟机栈

虚拟机栈,描述的是一个java方法被执行的线程内存模型:一个方法在被执行时,虚拟机就会创建一个栈帧(Stack Frame)用于存储【局部变量表】、【操作数盏】、【动态链接】和【方法出口】等信息。一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈从入栈到出栈的过程。

同样当一个方法调用到另一个方法时,也会创建另外一个栈帧。尤其在进行递归调用时,随着递归的深度越深,创建的栈帧数量也就会越多,当递归深度大于了虚拟机所允许的深度,将会抛出我们常见的StackOverFlowError。

1.2.1 局部变量表

TODO

1.3 本地方法栈

其作用和虚拟机站一样,其区别是它只作用于native方法;而虚拟机栈作用的是常规java方法,也即字节码对应的方法。不过在我们讨论的HotSpot中,直接将二者合二为一了。

1.4 Java堆

Java堆,是虚拟机管理的内存中最大的一块。它是所有线程共享的一块内存区域,在虚拟机启动时创建,用来存放对象实例。因此我们常说的垃圾收集器(GC)也就是作用与这个区域。

随着jdk版本和GC的不断迭代,java堆的设计模型也有一些悄然变化。在G1(Garbage First,垃圾回收器的一种)以前,java堆的设计都是基于“分代模型”来设计,也就是我们常说的:年轻代、老年代、永久代等。但在G1以后,java堆的设计就不再是“分代”了,而是分区。

这里所说的“分代模型”当然也值得总结一番,通常分为:

1.4.1 年轻代

包含eden伊甸区 和两个 surviver幸存区。用于存放刚创建的对象,或是一些“年轻的”对象(即经历GC次数较少)。

1.4.2 老年代

用于存放一些“老”对象,即经历过N多次GC仍未被清理掉的引用对象。以及在JDK1.7中,原本存放于永久代“字符串常量池”“静态变量”也开始存放于此。

1.4.3 方法区

通常它用于存放:类加载后的class信息、常量池、静态变量、即时编译器编译后的缓存代码等。

当然,需要注意的是,这里指的方法区其实是JVM定义的一种标准。通常它与java堆需要区分开来理解,写在这是因为在JDK1.8之前的版本,在HostSpot的实现中,它对应的就是永久代;而在JDK1.8(含1.8)以后的实现则是元空间(Meta Space)。元空间默认是受限于物理内存的,也就是说受限于实际的硬件资源大小。

1.5 运行时常量池

运行时常量池,是方法区的一部分。Class文件中的常量池表,在类加载后将会被存放到方法区的运行时常量池中。此外,比如String#intern()方法执行时,常量池中若不存在的字符串,也将会被放入此处。

1.6 直接内存

直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分。但是它也被频繁的使用到。在JDK1.4中加入的NIO(New Input/Output),引用了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过Java堆里的DirectByteBuffer作为这块堆外内存的引用。这样一来,程序就可以通过这个引用,直接操作堆外内存。这样的好处就在于某些场景中可以显著的提高性能,因为这样避免了Java堆和Native堆的数据来回复制。其实也就是我们常说的“零拷贝”的一种实现,即省去了从内核空间往用户空间拷贝数据资源消耗。


本文主要参考《深入理解Java虚拟机》。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值