2024.11.14 周四
今天的时间主要在做学校的大作业,下午做了一点苍穹外卖,刚接触Redis,准备一边学操作一边看Redis的八股,晚上主要看了JVM内存区域的八股文。
八股
JVM内存区域
介绍一下Java内存区域(运行时数据区)
根据 JVM8 规范,JVM 运行时内存共分为虚拟机栈、堆、元空间、程序计数器、本地方法栈五个部分。还有一部分内存叫直接内存,属于操作系统的本地内存,也是可以直接操作的。
线程私有的:
- 程序计数器(记录当前线程执行的位置)
- 虚拟机栈(栈->栈帧(局部变量表、操作数栈、动态链接、方法返回地址))
- 局部变量表:存放各种数据类型(boolean、byte、char、short、int、float、long、double、对象引用)
- 操作数栈:存放方法执行过程中产生的中间计算结果 和 计算过程中产生的临时变量
- 动态链接:Class 文件的常量池里保存有大量的符号引用,当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。
- 本地方法栈
线程共享的:
- 堆
- 方法区
- 直接内存(非运行时数据区的一部分)
本地方法栈
和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 两种错误。
什么是Native方法?
- 在Java中,native方法是一种特殊的方法,它的实现不是用Java编写的,而是用C或C++编写的,并且通常编译成了本地平台(如Windows、Linux或macOS)的机器码。
- native方法通过Java Native Interface (JNI)与Java代码进行交互。
- 使用native方法可以访问Java标准库中没有提供的底层操作系统特性,或者可以用于性能敏感的操作,因为本地代码通常比Java字节码执行得更快。
堆内存
Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
这部分空间可通过 GC(Garbage Collection) 进行回收。当申请不到空间时会抛出 OutOfMemoryError
。jdk1.8后,字符串常量池从永久代中剥离出来,存放在堆中。
- 新生代(Young Generation):新生代分为
Eden Space
和Survivor Space
,大多数新创建的对象首先存放在Eden区,而Eden区相对较小,当Eden区满时,会触发一次Minor GC(新生代垃圾回收);在Survivor Spaces中,通常分为两个相等大小的区域:S0(Survivor 0)和S1(Survivor 1),每次Minor GC后(即Eden区满后),存活对象(堆存放对象实例)会被移动到其中一个Survivor空间(S0或S1),来继续它们的生命周期。 - 老年代(Old Generation/Tenured Generation):在一次或多次的Minor GC中存活下来的对象会被移动到老年代。老年代中的对象生命周期较长,因此Major GC(也称为Full GC,涉及老年代的垃圾回收)发生的频率相对较低,但其执行时间通常比Minor GC长,因为老年代的空间通常比新生代大,以存储更多的长期存活对象。
- 元空间(Metaspace):从Java 8开始,永久代(Permanent Generation)被元空间取代,用于存储类的元数据信息,如类的结构信息(如字段、方法信息等)。元空间并不在Java堆中,而是使用本地内存,这解决了永久代容易出现的内存溢出问题。
- 大对象区(Large Object Space / Humongous Objects):在某些JVM实现中(如G1垃圾收集器)为大对象分配了专门的区域,称为 大对象区 或 Humongous Objects区域,大对象是指需要大量连续内存空间的对象,如大数组。这类对象直接分配在老年代,以避免因频繁的年轻代晋升而导致的内存碎片化问题。
方法区
方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。在不同的虚拟机实现上,方法区的实现是不同的。
当虚拟机要使用一个类时,它需要读取并解析Class文件获取相关信息,再将信息存入方法区,方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法区和永久代以及元空间是什么关系呢?
方法区和 永久代 以及 元空间的关系很像Java中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区,即永久代和方法区HotSpot虚拟机(目前最主流的JVM实现)对虚拟机规范中方法区的两种实现方式(JDK1.8以后方法区的实现变成元空间)。
方法区方法的执行过程?
- 解析方法调用:JVM会根据方法的符号引用找到实际的方法地址(如果之前没有解析过的话)。
- 栈帧创建:在调用一个方法之前,JVM会在当前线程的Java虚拟机栈中为该方法分配一个新的栈帧,用来存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 执行方法:对方法内的字节码指令作执行操作时,涉及的操作可能包括局部变量的读写、操作数栈的操作、跳转控制、对象创建、方法调用。
- 返回处理:方法执行完毕后,可能会返回一个结果给调用者,并清理当前的栈帧,恢复调用者的执行环境。
方法区(元空间实现)中还有哪些东西?
- 类信息:包括类的结构信息、类的访问修饰符、父类与接口等信息。
- 常量池:存储类和接口中的常量,包括字面值常量、符号引用,以及运行时常量池。
- 静态变量:存储类的静态变量,这些变量在类初始化的时候被赋值。
- 方法字节码:存储类的方法字节码,即编译后的代码。
- 符号引用:存储类和方法的符号引用,是一种不同于直接引用的引用类型。
- 运行时常量池:存储着 在类文件中的常量池数据(类加载后在方法区生成该运行时常量池)。
- 常量池缓存:用于提升类加载的效率,将常用的常量缓存起来方便使用。
算法
114.二叉树展开为链表(先序遍历,根左右)
项目
苍穹外卖(今天的工作量比较少,时间基本花在做大作业上)
另外通过苍穹外卖刚接触到Redis,在进行Redis的学习。