Java JVM --- JVM内存区域划分,类加载,GC垃圾回收

文章目录

JVM

  1. JVM主要是面试要考察,它是Java虚拟机中的内容
  2. 面试中经常考的主题:
    (1) JVM 内存区域划分
    (2) JVM 中类加载的过程
    (3) JVM 中垃圾回收机制

JVM内存区域划分

一个运行起来的Java进程就是一个JVM虚拟机,就需要从操作系统申请一大块内存,就会把这个内存,划分成不同的区域,每个区域都有不同的作用。

这个区域就类似租一个写字楼,进行装修

(这个大的场地,相当与申请了一大块内存空间)

JVM 申请了一大块内存之后,也会划分成不同的内存区域

  1. 方法区(jdk 1.7及其之前)/ 元数据区(jdk 1.8 开始往后)

    这里存储的内容,就是类对象(.class文件,加载到内存之后,就成了类对象了)

  2. 这里存储的内容,就是代码中new的对象

    (占据空间最大的区域)

  3. 这里存储的内容,就是代码执行过程中,方法之间的调用关系

比如 a->b->c->d,a方法调用b方法,b方法调用c方法,以此类推

  1. 程序计数器

比较小的空间,主要就是存放一个地址表示下一条要执行的指令,在内存中的哪个地方 (方法区里,每个方法,里面的指令,都是以二进制的形式存在的,保存到对应的类对象中的)

(1) 刚开始调用方法,程序计数器,记录的就是方法的入口地址 。随着一条一条的执行指令,每记录一条,程序计数器的值都会自动更新,去指向下一条指令

(2) 如果是一个顺序执行的代码,下一条指令就是把指令地址进行递增(栈顶是低地址,栈底是高地址)

(3) 如果是 条件/循环代码,下一条指令就可能会跳转到一个比较远的地址

常见面试题:
给你一个代码,问你某个变量,是处于内存中的哪个区域中?

有一个问题,容易让我们搞错,变量处于哪个空间上,和变量是不是引用类型,是不是基本类型,没关系~

每个线程都有自己私有的栈空间

在C++中可以访问别的线程的空间,所以在C++中不是私有的,而在Java中是有自己私有的栈空间的

  1. 内存区域划分
    (1) 理解这些区起到的作用
    (2) 给你一个代码,给你一个变量,问你这个变量是在哪个区的问题

类加载

  1. 类加载:java代码会被编译成.class文件(包含了一些字节码),java程序要想运行起来,就需要让jvm读取到这些.class文件,并且把里面的内容,构造成类对象,保存到内存的方法区中。
    jvm就会操作程序计数器去类对象中读取指令,并执行了
    所谓的执行代码,就是调用方法。就需要先知道每个方法,编译后生成的指令都是什么

类加载的基本流程

  1. 书上和官方文档把这个类加载的过程,主要分成了5个步骤(或者是三个步骤,把中间三个步骤合为一个步骤了)
    要想学习这个,最好是去读源码,openjdk是开源的,官方的jdk(oracle)是不开源的

下面的东西都要当做是八股文来背了

(1) 加载:找到 .class 文件,打开文件,读取文件内容

往往代码中,会给定某个类,全限定类名,例如:java.lang.String,java.util.ArrayList,jvm就会根据这个类名,在一些指定的目录范围内,查找

(2)验证:.class文件是一个二进制的格式(某个字节,都有某些特定含义的),就需要验证你当前读到的这个格式是否符合要求

Attributes:注解

(3)准备:给类对象分配内存空间(最终的目标,是要构造出类对象)

这里只是分配内存空间,还没有初始化呢。此时这个空间上的内存的数值,就是全0的。(此时如果尝试打印类的static成员,就是全0的)

(4)解析:针对类对象中包含的字符串常量进行处理。进行一些初始化操作 。java代码中用到的字符串常量,在编译之后,也会进入到.class文件中


举个栗子:

期权就是符号引用,股权就是直接引用,公司没有上市就是期权(在文件中),公司上市了就是股权(在内存中)

  1. 初始化:针对类对象进行初始化

把类对象中需要的各个属性都设置好,还需要初始化好static成员,还需要执行静态代码块,以及还可能需要加载一下父类(比如子类继承了父类的)

以上内容,要求我们需要熟练背诵,面试的时候很大概率会遇到原题

双亲委派模型

  1. 双亲委派模型:属于类加载中,第一个步骤(加载中的第一个步骤),其中的一个环节
  2. 作用:负责根据全限定类名,找到 .class文件
    这里的双亲,指的是parent这个单词,双亲之一,parents才是真正的双亲。
    实际上这里翻译成父亲委派模型更合适一些

JVM中内置了三个类加载器:

  1. 类加载的过程(找 .class文件的过程)

  2. 搞双亲委派模型的主要原因:是为了保证优先级,让标准库优先查找,再让扩展库找,最后让自己写的类和第三方库找

    所谓的双亲委派模型,其实就是一个简单的查找优先级问题,只不过面试中经常考它,是因为它有一个高大上的名字

  3. 双亲委派模型,也不是不可打破的。如果我们自己写一个类加载器,不一定非要遵守上述的流程。

比如:tomcat里,加载webapp的时候就是用的自定义的类加载器。就只能在webapp指定的目录中找,这里找不到,就算了,直接抛异常,不会去标准库啥的里面去找了!!

  1. 可以从最高级的那个加载器开始加载吗?这是可以的,JVM中用了递归的方法,效果都是一样的

GC垃圾回收

  1. 在C语言和C++中都有动态申请内存,如果没有及时地释放空间,那么就会产生内存泄漏的风险。

  2. 在C++中即使调用了delete,还是有可能出现内存泄漏的问题

  3. GC垃圾回收机制的好处:JVM会自动判定这个内存是否要继续使用,如果不用了就会被回收

  4. C++为什么不引入GC ?

    STW问题就是中间突然卡顿一下

  5. GC真正要释放的是堆上的对象

GC的过程

gc可以理解成两个大的步骤:

1.找到垃圾

2.释放垃圾
垃圾:指的是一个不再使用的对象,只是描述了一种客观的状态,没有主观贬低的意思。

如果面试官让你介绍垃圾回收,你可以介绍引用计数。如果让你介绍java的垃圾回收,介绍引用计数就不合适了

  1. 找到垃圾
    在GC的圈子中,有两种主流的方案:
    (1) 引用计数[Python,PHP]
    深入理解java虚拟机中也介绍了引用计数
    引用计数描述这个对象有几个引用指向它,引用计数为0,就是没有对象引用了,就垃圾回收了

    为什么Java不使用引用计数?
    引用计数,存在两个重要的问题
    1.比较浪费内存

    2.引用计数机制,存在循环引用的问题
    循环引用:存在一个a引用一个对象,一个b引用一个对象,然后a里的的t引用b对象,b里面的t引用a对象,再把a对象和b对象置为null,销毁两引用。那么这两个对象中的t互相引用,要拿到a对象中的t,需要b对象中的t,要拿到b对象中的t,需要a对象中的t。这就是循环引用

    (2) 可达性分析[Java]
    可达性分析:本质上是 时间换空间 这样的手段。有一个 / 一组线程,周期性的扫描我们的代码中所有的对象。

    举个例子:
    二叉树的遍历

    可达性分析,出发点有很多,不仅仅是所有的局部变量,还有常量池中引用的对象,还有方法区中的静态引用类型引用的变量...
    这些可以作为出发点的统称为 GCRoots

这里的可达性分析,都是周期性进行的可达性分析比较消耗系统资源,开销比较大,时间上消耗比较大),当前某个对象是否是垃圾,是随着代码的执行,会发生改变(可能当前这个不是垃圾,随着代码的执行,这个就变成了垃圾)

总结:可以到达的点进行全部的遍历,并且是周期性地遍历

  1. 如何回收垃圾
    三种基本的思路
    (1) 标记清除 ,比较简单粗暴的释放方式(会产生大量的内存碎片,导致后续有足够的空间,但是无法申请)

(2) 复制算法 (为了解决标记清除)

通过复制的方式,把有效的对象,归类到一起。再统一释放剩下的空间

(先把空间砍一半,比如左边总共有5个对象,但是有2个对象是垃圾,那么要把有效的3个对象复制到右边,然后把左边5个对象都释放掉)

(3) 标记整理

既能够解决内存碎片问题,又能处理复制算法中利用率的问题

(比如有6个对象,3个对象有效,3个对象是垃圾,把有效的对象都搬运到前面来,后面是垃圾的一起释放掉)

实际上,JVM采用的释放思路,是上述基础思路的结合体,让不同的方案,扬长避短

上述结合(JVM释放)的过程:

分代回收,对象能活过的GC扫描轮次越多,就是越老

新生代 ,主要使用复制算法 进行回收有效的对象,老年代 ,主要使用标记整理的方式回收有效的对象(标记清除,非常不好用,并没有真正采用)

分代回收,是JVM中主要的回收的思想方法,但是在垃圾回收器的具体实现的时候,可能还有一些调整和优化

其实,分代回收,就和找工作的流程是一样的,都会经历上述的新生代,幸存区和老年代的过程

主要掌握怎么找垃圾 ,怎么释放垃圾就可以了

相关推荐
vortex52 小时前
Bash 中的 shopt -s globstar:递归 Glob 模式详解
开发语言·chrome·bash
livingbody2 小时前
【2025年9月版 亲测可用】《人民日报》PDF文件下载
开发语言·爬虫·python·pdf
摸鱼的老谭2 小时前
Java学习之旅第一季-25:一维数组
java·开发语言·数组
山猪打不过家猪2 小时前
(一)算法
java·开发语言·算法
czliutz3 小时前
R语言绘制股票K线图及布林线
开发语言·r语言
Momentary_SixthSense3 小时前
如何对较长的Stream链进行Debug
android·java·开发语言
JAVA学习通3 小时前
微服务项目->在线oj系统(Java-Spring)--竞赛管理
java·sql·spring
鄃鳕3 小时前
C++坑系列,C++ std::atomic 拷贝构造函数问题分析与解决方案
java·javascript·c++
木易小熙3 小时前
Guava Cache
java·spring·guava