面经:JVM学习笔记

JAVA每日面经——JVM篇(包含JVM优化) Java虚拟机(JVM)是Java编程语言的核心组件之一,是一个能够执行Java字节码的虚拟机器。它是Java跨平台特性的关键所在,允许Java程序在不同的操作系统上运行,而不需要重新编译成特定平台的机器码。JVM负责将Java源代码编译成字节码,然后在运行时解释或编译执行这些字节码。 阅读详情

文章目录

一、Java内存区域详解

1. 运行时数据区

  • JKD1.8之前
    在这里插入图片描述
  • JDK1.8之后
    在这里插入图片描述

2. 程序计数器 (线程私有)

  • 作用
    字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理
    在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了
  • 是否会出现OutOfMemoryError
    是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

3. Java虚拟机栈 (线程私有)

  • 生命周期
    Java 虚拟机栈(后文简称栈)也是线程私有的,它的生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡。
  • 作用
    方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。
  • 栈的构成
    栈由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法返回地址。
    在这里插入图片描述
    局部变量表:主要存放了编译期可知的各种数据类型、对象引用
    操作数栈:用于存放方法执行过程中产生的中间计算结果
    动态链接:主要服务一个方法需要调用其他方法的场景,将符号引用转换为调用方法的直接引用
  • 栈的两种错误
  • StackOverFlowError 若栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时
  • OutOfMemoryError 如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间
  • 补充
  • 垃圾回收是否涉及栈内存?
    答:不会,栈帧内存在每次方法结束后都会弹出栈,自动回收掉
  • 栈内存分配越大越好吗?
    答:不对,因为物理内存是一定的,栈内存越大,可以支持更多的递归调用,但是可执行的线程数目就会越少。
  • 方法内的局部变量是否线程安全?
    如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
    如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

4. 本地方法栈 (线程私有)

虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

5. 堆(线程共享)

  • 生命周期及作用
    在虚拟机启动时创建
    此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
    线程共享、考虑线程安全、有垃圾回收机制
  • 堆内存的划分
    在这里插入图片描述
    (1) JDK7版本之前
    新生代内存(Young Generation)、老生代(Old Generation)、永久代(Permanent Generation)
    下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。
    (2) JDK 8 版本之后 PermGen(永久) 已被 Metaspace(元空间) 取代,元空间使用的是直接内存
  • OutOfMemoryError错误
    java.lang.OutOfMemoryError: GC Overhead Limit Exceeded : 当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。
    java.lang.OutOfMemoryError: Java heap space :假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发此错误。

6. 方法区(线程共享)

方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。到底要如何实现那就是虚拟机自己要考虑的事情了。也就是说,在不同的虚拟机实现上,方法区的实现是不同的。

  • 作用
    当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
    静态变量放在方法区,成员变量放在堆,局部变量放在栈。
    运行时常量池是方法区的一部分。
    方法区:类加载.class文件的相关信息
  • 方法区与永久代和元空间的关系
    在这里插入图片描述
    方法区和永久代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区,也就是说永久代以及元空间是 HotSpot 虚拟机对虚拟机规范中方法区的两种实现方式。并且,永久代是 JDK 1.8 之前的方法区实现,JDK 1.8 及以后方法区的实现变成了元空间。

7. 运行时常量池

常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量
等信息
运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量
池,并把里面的符号地址变为真实地址
比较抽象可以看黑马视频,很清晰
常量池-黑马

8. 字符串常量池(String Table)

  • 作用
    是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。本质是一个hashset,保存的是字符串对象的引用,字符串对象的引用指向堆中的字符串对象。
  • JDK1.7为什么要将字符串常量池移动到堆中
    JDK1.7 之前,字符串常量池存放在永久代。JDK1.7 字符串常量池和静态变量从永久代移动了 Java 堆中。
    主要是因为永久代(方法区实现)的 GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC。Java 程序中通常会有大量的被创建的字符串等待回收,将字符串常量池放到堆中,能够更高效及时地回收字符串内存。
  • 字符串拼接题目
    黑马
//s1 s2 s3都会被加载到字符串常量池里面去
//StringTable[a,b,ab] 
//s4是new出来的对象 放在堆里面
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;//拼接的原理是new stringbulider().append("a").append("b").toString() new String("ab")
String s5 = "a" + "b";//编译期间已确定是ab 直接在字符串常量池中找
String s6 = s4.intern();//主动将串池中还没有的字符串对象放入串池,1.8以后将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//true
System.out.println(s3 == s6);//true
String x2 = new String("c") + new String("d");//堆cd
String x1 = "cd";//字符串池cd
x2.intern(); 
// 问,如果调换了【最后两行代码】的位置呢
System.out.println(x1 == x2);//false
//调换后:true
  • 特性总结
    常量池中的字符串仅是符号,第一次用到时才变为对象
    利用串池的机制,来避免重复创建字符串对象
    字符串变量拼接的原理是 StringBuilder (1.8)
    字符串常量拼接的原理是编译期优化
    可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池,1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

9. 直接内存

不是虚拟机的,是系统的内存
JDK1.8元空间使用直接内存

  • 常见于 NIO 操作时,用于数据缓冲区
  • 分配回收成本较高,但读写性能高
  • 不受 JVM 内存回收管理

10. 元空间和永久代

元空间在jdk7及以前是属于永久代的,元空间和永久代都是用来存储class相关信息,包括class对象的Method,Field等,元空间和永久代其实都是方法区的实现,只是实现有所不同,所以说方法区其实只是一种JVM的规范。
元空间与永久代区别是其内存空间直接使用的是本地内存,而metaspace没有了字符串常量池,而在jdk7的时候已经被移动到了堆中,MetaSpace其他存储的东西,包括类文件,在JAVA虚拟机运行时的数据结构,以及class相关的内容,如Method,Field道理上都与永久代一样,只是划分上更趋于合理,比如说类及相关的元数据的生命周期与类加载器一致,每个加载器就是我们常说的classloader,都会分配一个单独的存储空间。

11. 堆和栈的区别

堆:线程共享,存放对象实例,存储的单位(解决数据存储的问题)
栈:线程独享,存放基本数据类型和对象的引用,运行时的单位

二、Hotspot虚拟机对象

1. 对象创建整体流程

在这里插入图片描述

  • 类加载检查
    虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
  • 分配内存
    在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。
    分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
  • 初始化零值
    内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值
  • 设置对象头
    初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
  • 执行init方法
    执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来

2.内存分配的两种方式

  • 指针碰撞
    适用场合 :堆内存规整(即没有内存碎片)的情况下。
    原理 :用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可。
    使用该分配方式的 GC 收集器:Serial, ParNew
  • 空闲列表
    适用场合 : 堆内存不规整的情况下。
    原理 :虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的,在分配的时候,找一块儿足够大的内存块儿来划分给对象实例,最后更新列表记录。
    使用该分配方式的 GC 收集器:CMS

选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的。

3.内存分配并发问题

  • CAS+失败重试
    CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
  • TLAB Thread local allocation buffer线程本地分配缓存区
    为每一个线程预先在 Eden 区分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配

4. 对象的内存布局

  • 对象头
    存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等)
    类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
  • 实例数据
    对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容
  • 对齐填充
    不是必然存在的,也没有什么特别的含义,仅仅起占位作用。

5. 对象的访问定位

  • 句柄
    如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与对象类型数据各自的具体地址信息。
    在这里插入图片描述
  • 直接指针
    如果使用直接指针访问,reference 中存储的直接就是对象的地址。
  • 对比
    这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。

6. 对象的大小

在这里插入图片描述
任何对象大小都大于8byte

7.new一个对象

在这里插入图片描述

三、JVM垃圾回收详解

1. 内存分配和回收原则

1.1 三种收集器的内存分配策略

对象优先在Eden区分配
大对象直接进入老年代(大对象指需要大量连续内存的Java对象)
长期存活的对象进入老年代

1.2 空间分配担保

确保在 Minor GC 之前老年代本身还有容纳新生代所有对象的剩余空间。
就是怕Minor GC后进入老年代的对象太多了,老年代没有那么大空间,先提前检查一下,如果检查结果显示老年代确定装不下,那么这次Minor GC就得改成Full GC,如果Full GC完了老年代空间,OOM内存溢出。

2. Minor GC与Full GC对比

2.1 定义

Minor GC (新生代GC):发生在新生代的垃圾收集动作,发生频繁,速度快
Full GC/Major GC(老年代GC):发生在老年代,速度慢

2.2 什么时候触发

Minor GC:在新生代的Eden区满了会触发
Full GC: System.gc()方法调用、老年代空间不足、空间分配担保失败

2.3 为什么要减少Full GC

影响性能,导致STW(Stop the world),对外的表现是卡顿
STW: 说人话就是,清理垃圾的时候,线程要停下来

3. 死亡对象判断方法

  • 引用计数法
    引用加1,没引用减1。计数为0时 表示不再使用
    弊端: 无法解决对象相互循环引用的问题
  • 可达性分析算法
    根对象:永远不会被回收
    “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
  • 哪些对象可以作为 GC Roots 呢?
    虚拟机栈(栈帧中的本地变量表)中引用的对象
    本地方法栈(Native 方法)中引用的对象
    方法区中类静态属性引用的对象
    方法区中常量引用的对象
    所有被同步锁持有的对象

4. 引用类型总结

  • 强引用
    必不可少。垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
  • 软引用
    可有可无的生活用品。发生垃圾回收的时候,如果内存不足,就会被垃圾回收器回收
    可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。
  • 弱引用
    可有可无的生活用品。发生垃圾回收的时候,就会被垃圾回收器回收
    可以和一个引用队列(ReferenceQueue)联合使用
  • 虚引用
    如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。必须配合引用队列使用,

5. 如何判断一个常量是废弃常量

运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?
假如在字符串常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池了

6. 如何判断一个类是无用的类

方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?

  • 三个条件:
    该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
    加载该类的 ClassLoader 已经被回收。
    该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

7. 垃圾收集算法

  • 标记-清除
    首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象在这里插入图片描述
    速度较快。适合存活对象多,需要回收的对象少的场景。
    会造成
    内存碎片
    。
  • 标记-复制
    将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
    在这里插入图片描述
  • 标记-整理
    标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存在这里插入图片描述
  • 分代收集算法
    当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
    比如在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制“算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择**“标记-清除”或“标记-整理”**算法进行垃圾收集。

8. 垃圾收集器

8.0 Serial 收集器

Serial(串行)收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。

8.1 CMS(Concurrent Mark Sweep)收集器

  • 目的
    以获取最短回收停顿时间为目标,是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。 牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器
  • 过程
    在这里插入图片描述
    初始标记: 暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;
    并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。
    重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
    并发清除: 开启用户线程,同时 GC 线程开始对未标记的区域做清扫。
  • 浮动垃圾
    在并发标记阶段本来可达的对象,由于用户线程的作用变得不可达了,即产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,最终导致这些新产生的垃圾对象没有被及时回收。
    添加链接描述
  • 优缺点
    优点:并发收集、低停顿
    缺点:对 CPU 资源敏感;无法处理浮动垃圾;它使用的回收算法标记-清除算法会导致收集结束时会有大量空间碎片产生。

8.2 G1收集器

  • 概述
    G1 可谓博采众家之长,力求到达一种完美。
  • 它吸取了增量收集优点,把整个堆划分为一个一个等大小的区域(region)。内存的回收和划分都以region为单位;
  • 同时,它也吸取了 CMS 的特点,把这个垃圾回收过程分为几个阶段,分散一个垃圾回收过程;
  • 而且,G1 也认同分代垃圾回收的思想,认为不同对象的生命周期不同,可以采取不同收集方式,因此,它也支持分代的垃圾回收。
  • 如何理解G1名称的由来:为了达到对回收时间的可预计性,G1 在扫描了 region 以后,对其中的活跃对象的大小进行排序,首先会收集那些活跃对象小的 region,以便快速回收空间(要复制的活跃对象少了),因为活跃对象小,里面可以认为多数都是垃圾,所以这种方式被称为 Garbage First(G1)的垃圾回收算法,即:垃圾优先的回收。
  • 特点
    并行与并发、分代收集、空间整合、可预测的停顿
  • 步骤
    初始标记、并发标记、最终标记、筛选回收
  • 三色标记法
    在这里插入图片描述

8.3 ZGC收集器

添加链接描述

  • 内存布局
    在这里插入图片描述
  • 染色指针
    在这里插入图片描述
  • 读屏障
    在这里插入图片描述
  • 内存多重映射
    在这里插入图片描述
  • GC的过程
    在这里插入图片描述
    ZGC 采用标记 - 整理算法,算法的思想是把所有存活对象移动到堆的一侧,移动完成后回收掉边界以外的对象

9. 被标记为垃圾的对象一定会被回收吗?

即使在可达性分析算法中不可达的对象,也并非是“非死不可”,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡**,至少要经历两次标记**过程。

第一次标记:如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那它将会被第一次标记;

第二次标记:第一次标记后接着会进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 方法。在 finalize() 方法中没有重新与引用链建立关联关系的,将被进行第二次标记。第二次标记成功的对象将真的会被回收,如果对象在 finalize() 方法中重新与引用链建立了关联关系,那么将会逃离本次回收,继续存活。

10. 垃圾回收是从哪里开始的?

在这里插入图片描述
栈是真正进行程序执行地方,所以要获取哪些对象正在被使用,则需要从 Java 栈开始。同时,一个栈是与一个线程对应的,因此,如果有多个线程的话,则必须对这些线程对应的所有的栈进行检查。
除了栈外,还有系统运行时的寄存器等,也是存储程序运行数据的。这样,以栈或寄存器中的引用为起点,我们可以找到堆中的对象,又从这些对象找到对堆中其他对象的引用,这种引用逐步扩展,最终以 null 引用或者基本类型结束,这样就形成了一颗以 Java 栈中引用所对应的对象为根节点的一颗对象树。
栈 / 寄存器 -------- 堆

11. 对finalize方法的理解

一个对象真正死亡,至少要经历两次标记过程:如果对象不可达,那它将会被第一次标记并且进行一次筛选。筛选的条件是此对象是否需要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。

如果对象被判定为有必要执行finalize()方法,会放置在F-Queue的队列之中,并稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,原因是如果一个对象在finalize()方法中执行缓慢,或者发生了死循环,可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。

finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那么在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那么基本上它就真的被回收了

四、类加载

1. 类的生命周期

2. 类的加载过程

在这里插入图片描述

  • 加载
    通过全类名获取定义此类的二进制字节流
    将字节流所代表的静态存储结构转换为方法区的运行时数据结构
    在内存中生成一个代表此类的Class对象,作为方法区这些数据的访问入口

  • 验证
    文件格式验证—元数据验证-----字节码验证-------符号引用验证
    在这里插入图片描述

  • 准备
    准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。

  • 解析
    解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。

  • 初始化
    初始化阶段是执行初始化方法 ()方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。
    在准备阶段,变量已经赋过一次系统要求的初始值了,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器 () 方法的过程。

  • 卸载
    该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。
    该类没有在其他任何地方被引用
    该类的类加载器的实例已被 GC

3. 类加载器总结

  • BootstrapClassLoader(启动类加载器)
    最顶层的加载类,由 C++实现,负责加载 %JAVA_HOME%/lib目录下的 jar 包和类或者被 -Xbootclasspath参数指定的路径中的所有类。
  • ExtensionClassLoader(扩展类加载器)
    主要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 系统变量所指定的路径下的 jar 包。
  • AppClassLoader(应用程序类加载器)
    面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。

4. 双亲委派模型

4.1 定义

如果一个类加载器收到了类加载的请求,它首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器去完成,每一层次的类加载器都是这样,所以所有的加载请求最终都应该传送到顶层的启动类加载容器,只有当父类加载器无法完成加载,子加载器才会尝试自己去加载
在这里插入图片描述

4.2 好处

双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API 不被篡改。如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类。

4.3 弊端

基础类如果想要调用用户的代码,那么就会出现问题,因为第三方的类不能被启动类所加载。

4.4 如何自定义类加载器?如果不想用双亲委派模型怎么办?

  • 自定义加载器
    需要继承 ClassLoader 。如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。
  • 打破双亲委派模型
    自己写一个类加载器;
    重写 loadClass() 方法
    重写 findClass() 方法

4.5 代码实现

实现双亲委派的代码都集中在 java.lang.ClassLoader 的 loadClass() 方法中,逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父加载器的 loadClass() 方法,若父加载器为空则默认使用启动类加载器作为父类加载器。如果父类加载失败,抛出 ClassNotFoundException 异常后,再调用自己的 findClass() 方法进行加载。

4.6 打破双亲委派模型的场景

JNDI 服务,它的代码由启动类加载器去加载,但 JNDI 的目的就是对资源进行集中管理和查找,它需要调用独立厂商实现并部署在应用程序的 classpath 下的 JNDI 接口提供者(SPI, Service Provider Interface) 的代码,但启动类加载器不可能“认识”之些代码,该怎么办?

为了解决这个困境,Java 设计团队只好引入了一个不太优雅的设计:**线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过 java.lang.Thread 类的 setContextClassLoader() 方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。有了线程上下文类加载器,JNDI 服务使用这个线程上下文类加载器去加载所需要的 SPI 代码,也就是父类加载器请求子类加载器去完成类加载动作,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型,但这也是无可奈何的事情。

Java 中所有涉及 SPI 的加载动作基本上都采用这种方式,例如 JNDI、JDBC、JCE、JAXB 和 JBI 等。

五、JVM主要组成部分及作用

在这里插入图片描述

  • 类装载系统(类加载器)
    加载类文件到内存
  • 执行引擎
    也叫解释器,负责解释指令,交由操作系统执行
  • 本地库接口
    与其他语言交互时使用
  • 运行时数据区
    JVM的内存区域
  • 工作原理
    编译器把java代码变成字节码,类加载器把字节码加载到内存,将其放在运行时数据区的方法区内,
    而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,
    因此需要特定的命令解析执行引擎,将字节码翻译成底层系统指令,再交给cpu去执行,
    而这个过程需要调用其他语言的本地库接口来实现整个程序的功能

六. JVM调优部分

1.JVM调优步骤

在这里插入图片描述

2. 调优的目标及策略

  • 目标
    停顿时间(一般由垃圾回收引起),吞吐量(用户程序运行时间占总时间之比,可以理解为系统在特定时间内的最大工作量),内存占用
  • 调优策略:内存和垃圾回收器
    选择合适的垃圾回收器
    调整内存大小(垃圾收集频率非常频繁,如果是内存太小,可适当调整内存大小)
    调整内存区域大小比率(某一个区域的GC频繁,其他都正常。)
    调整对象升老年代的年龄(老年代频繁GC,每次回收的对象很多。)
    调整大对象的标准(老年代频繁GC,每次回收的对象很多,而且单个对象的体积都比较大。)
    调整GC的触发时机(CMS,G1 经常 Full GC,程序卡顿严重。)
    调整 JVM本地内存大小(GC的次数、时间和回收的对象都正常,堆内存空间充足,但是报OOM)

3. GC日志

在这里插入图片描述

4. 虚拟机监控命令

在这里插入图片描述
第一个指令是jps 不是jsp
jstack分析死锁和CPU飙高的实战

  • jps指令
    jps是JDK提供的一个可以列出正在运行的Java虚拟机的进程信息的命令行工具
  • jstack指令
    jstack是JVM自带的Java堆栈跟踪工具,它用于打印出给定的java进程ID、core file、远程调试服务的Java堆栈信息.

5. 调优参数

在这里插入图片描述
Xmx Java Heap最大值,默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定,建议设置为可用内存的最大值的80%;
-Xms Java Heap初始值,默认值为物理内存的1/64,Server端JVM最好将-Xms和-Xmx设为相同值;
-Xmn Java Heap Young区大小,默认值为堆内存的3/8,对于最新的G1收集器,建议不要设置此参数,而由系统自动分配和管理;
-Xss 每个线程的Stack大小。

6. JDK可视化分析工具

在这里插入图片描述

7. CPU飙高,不断FULL GC,如何排查

top命令 + jstat + jstack
详细解答

【JVM专题】一篇文章掌握整个JVM,JVM超详细解析!!! 一文熟悉JVM 的基本概念,JVM 的运行过程,JVM 内存区域,垃圾回收与算法,JAVA 四中引用类型,JAVA IO/NIO,JVM 类加载机制等 阅读详情

相关推荐

JVM高频面试基本问题与知识点整理

JVM高频面试基本问题与知识点整理

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 87万+

JVM面经汇总

JVM面经汇总 java虚拟机在执行java程序的过程中,会把它管理的内存划分成若干个不同的数据区域。 JVM的主要组成部分及作用 JVM包含两个子系统和两个组件,两个子系统为ClassLoader类装载、Execution Engine执行引擎。 两个组件为Runtime Data Area运行时数据区、Native Interface本地接口。 JVM运行时数据区 JVM在执行java程序时会将它所管理的内存划分为若干个不同的数据区域。 程序计数器 程序计数器是一块较小的内存空间,可以看作当前线程所

所幸你是例外的博客 1292

CANape快速上手

文章帮助记录CANape学习过程。

小弱鸡的博客 1万+

八股文之jvm

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。......

极客鼠之家 3412

RAGFlow源码解析-2、从源码构架ragflow v0.25.0镜像

本文详细记录了从零开始构建自定义RAGFlow镜像lt/ragflow:0.25.0的全过程。首先介绍了环境准备,包括检查Node.js、Docker版本和项目结构。然后详细说明了构建步骤:编译前端、创建自定义Dockerfile、构建镜像和验证。接着讲解了docker-compose配置修改要点,包括网络定义、MySQL依赖处理等。最后总结了部署过程中遇到的五个关键问题及解决方案,包括networks未定义、MySQL服务依赖、tiktoken文件缺失、数据库配置错误和端口映射问题。通过完整的操作流程和问

liutao841204的专栏 364

win11下的MySQL安装

目前官方最新版本是8.0.26,考虑到其稳定性、可靠性还需一定周期保证,所以使用官方版求稳定仍然建议5.7系列。3、选择MySQL Servers 5.7.38 -x64,然后按绿色箭头将选中的版本移植到右边选中框中。4、点击下方蓝色Advanced Options,选择安装位置。1、在开始界面搜索MySQL,点击command line。4、在“系统变量”中选择“path”,点击编辑。5、新建——>增加MySQL路径——>确定,12、execute,安装好后next。9、不需要做修改,直接点击next。.

rensweet的博客 2万+

阿里三面惨遭被虐,spring,jvm,mybatis,并发编程等一窍不通

三面大概九十分钟,问的东西很全面,需要做充足准备,就是除了概念以外问的有点懵逼了(呜呜呜~)。回来之后把这些题目做了一个分类并整理出答案(强迫症的我~狂补知识~)分为spring,jvm,mybatis,并发编程等,接下来分享一下我的这阿里面试的面经+一些我的学习笔记。 注意注意:需要小编整理的spring,jvm,mybatis,并发编程等,面试答案、学习笔记、思维脑图(xmind) 阿里关于spring面试问题: 请解释下spring框架中的Ioc? BeanFactory 和.

ppItStudy的博客 236

一文告诉你:如何在trae中编译运行STM32程序,并让AI帮你修改代码

本文详细介绍了如何将STM32程序从Keil迁移到国产免费工具Trae,并利用AI进行代码修改的全过程。

水滴爬呀爬 4884

蚂蚁金服二面面完感觉身体被掏空,鬼知道我这50分钟经历了些什么。。

二面大概50分钟,问的东西很全面,需要做充足准备,就是除了概念以外问的有点懵逼了(呜呜呜~)。回来之后把这些题目做了一个分类并整理出答案(强迫症的我~狂补知识~)分为spring,jvm,并发编程等,接下来分享一下我的这蚂蚁金服面试的面经+一些我的学习笔记。 蚂蚁金服关于spring部分面试问题: Spring bean的生命周期能不能结合源码回答一下这个问题 Spring容器当中包含了哪些常用组件(至少说5个),作用是什么,场景是什么; Spring自动注入的原理是什么?能不能从源码来说.

Java0258的博客 793

让 ABAP 内表真正释放内存:从 CLEAR、FREE 到打破 Table Sharing 的实战技巧

ABAP内表内存管理的关键在于理解其运行时结构:DELETE操作仅逻辑删除行而不会释放内存,CLEAR或FREE才能真正回收内存。常见的误区包括误判空表状态和忽视内表共享机制。实际应用中,大表筛选后应采用VALUE表达式或逐行插入方式重建表体以释放内存。典型案例显示,正确处理内表内存可显著降低SAP Gateway和批处理程序的内存消耗,提升系统稳定性。开发时需结合调试工具观察内存状态,避免因共享机制导致无效回收。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 324

Java 部分笔试题解析

合法的 try 语句块必须包含 try 块,并且至少包含一个 catch 块或一个 finally 块。给定一个长度为n的数组,数组中的每个元素,表示一根水管的长度,水管可以在任意位置锯开。调用 sleep() 方法、创建新线程、高优先级线程进入就绪状态只会暂停或抢占线程的执行,而不会终止线程的运行。A.抽象类中可以包含非抽象的普通方法,接口中的方法必须是抽象的,不能有非抽象的普通方法。接口中的成员变量必须是常量(static final),而抽象类可以有普通成员变量。

博客 2578

金三银四上岸“钉钉”,分享我的Java面经

Java面试核心知识点一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!Java面试核心知识点已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!到不错的offer了**[外链图片转存中…(img-092HraDe-1712521736476)]《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

2401_83329718的博客 887

【Java基础知识 19】Java接口的作用和意义

Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

学Java,找哪吒 2万+

最新4面美团面经总结:附学习笔记/面试真题/技能进阶书籍

面试时间将近两个小时(期间等待二面面试官来面我的时候等了半个多小时)面试官问的东西很多,还挖了好几个坑,一个技术点套着一个技术点的问,一定要做好万全的准备。问了一些基本层面上的技术点都答出来了,稍微问深一点我就有点懵了(实战经验还是不足)。 回来之后把这些题目做了一个分类并整理出答案(每次去面试的时候面试官问的问题面试结束后我都会做笔记)分为Spring+逻辑算法+MySQL+Java+Redis+并发编程+JVM+RabbitMQ等,接下来分享一下我的这次美团二面面经+一些我的学习笔记。 注意注..

Java6888的博客 405

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

spring cloud微服务_京东三面凉凉:java+spring+jvm+kafka+微服务等一个都讲不清

三面大概九十分钟,问的东西很全面,需要做充足准备,就是除了概念以外问的有点懵逼了(呜呜呜~)。回来之后把这些题目做了一个分类并整理出答案(强迫症的我~狂补知识~)分为spring+jvm+微服务+kafka+MongoDB等五类,接下来分享一下我的这京东面试的面经+一些我的学习笔记。注意注意:需要小编整理的spring+jvm+微服务+kafka+MongoDB等五类面试答案、学习笔记、思维脑图...

weixin_39781326的博客 77

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

字节跳动四面凉凉:Java基础+Spring原理+JVM+Kafka+微服务+分布式等你全讲不清,还好意思去海投简历!赶紧回来恶补吧!

以下来自粉丝投稿: 三面大概九十分钟,问的东西很全面,需要做充足准备,就是除了概念以外问的有点懵逼了(呜呜呜~)。回来之后把这些题目做了一个分类并整理出答案(强迫症的我~狂补知识~)分为spring+jvm+微服务+kafka+MongoDB等五类,接下来分享一下我的这京东面试的面经+一些我的学习笔记。 注意注意:需要小编整理的spring+jvm+微服务+kafka+MongoDB等五类面试答案、学习笔记、思维脑图(xmind) 需要获取得话麻烦一键三连之后加小助手vx:bjmsb201.

如果频繁就私信一下呢 222

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

2021最新分享字节面经:5000字总结及答案解析

前言 为什么要面试字节跳动? 近两年是中国互联网企业组织架构升级的大年,阿里、腾讯、小米、快手等知名互联网企业都进行了相应调整。2020年3月12日,字节跳动成立八周年之际,宣布组织全面升级,而这一消息也彻底激起了我对字节跳动的期待。 实际上,我算是字节跳动名副其实的脑x粉,手机里的最多的app是字节旗下的,包括头条、抖音、西瓜视频、懂车帝。而字节跳动也是我最看好的互联网公司之一,因此我首先向字节跳动抛出了我的“橄榄枝”。 注意:文章以下内容所提供的 [面试真经、思维导图、学习笔记、面试题库]等文

bieber007的博客 597

面经感悟:四面字节跳动(附面试复习资料)

前言 为什么要面试字节跳动? 近两年是中国互联网企业组织架构升级的大年,阿里、腾讯、小米、快手等知名互联网企业都进行了相应调整。2020年3月12日,字节跳动成立八周年之际,宣布组织全面升级,而这一消息也彻底激起了我对字节跳动的期待。 实际上,我算是字节跳动名副其实的脑x粉,手机里的最多的app是字节旗下的,包括头条、抖音、西瓜视频、懂车帝。而字节跳动也是我最看好的互联网公司之一,因此我首先向字节跳动抛出了我的“橄榄枝”。 注意:文章以下内容所提供的 [面试真经、思维导图、学习笔记、面试题库]等文

Java0258的博客 624

2024最新分享我的面经总结:Java面试技术点攻略(九大核心专题)

千千万万要记得:多刷题!!多刷题!!之前算法是我的硬伤,后面硬啃了好长一段时间才补回来,算法才是程序员的灵魂!!!!篇幅有限,以下只能截图分享部分的资源!!(1)多线程(这里以多线程为代表,其实整理了一本JAVA核心架构笔记集)(2)刷的算法题(还有左神的算法笔记)(3)面经+真题解析+对应的相关笔记(很全面)(4)视频学习(部分)ps:当你觉得学不进或者累了的时候,视频是个不错的选择在这里,最后只一句话:祝大家offer拿到手软!!本文已被。

2301_82244514的博客 885

进不了大厂?肝完这份20年面试经验总结,小厂出来的我面进了字节你敢信?

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。本文已被。

2301_82243232的博客 391

kafka 配置jvm_京东三面凉凉:Java+spring+jvm+kafka+微服务等一个都讲不清

以下来自粉丝投稿:三面大概九十分钟,问的东西很全面,需要做充足准备,就是除了概念以外问的有点懵逼了(呜呜呜~)。回来之后把这些题目做了一个分类并整理出答案(强迫症的我~狂补知识~)分为spring+jvm+微服务+kafka+MongoDB等五类,接下来分享一下我的这京东面试的面经+一些我的学习笔记。注意注意:需要小编整理的spring+jvm+微服务+kafka+MongoDB等五类面试答案、学...

weixin_42137723的博客 261

面渣逆袭:JVM经典五十问,这下面试稳了

打算年后跳槽的,建议看看!不跳槽也可以先收藏。 五十问、六十一图、两万字,彻底搞定JVM!

三分恶的博客 2万+

JVM 面经

方法区并不真实存在,是 Java 虚拟机规范中的一个逻辑概念,用于存储已被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等。在 HotSpot 虚拟机中,方法区的实现称为永久代 PermGen,但在 Java 8 及之后的版本中,已经被元空间 Metaspace 所替代。堆是 JVM 中最大的一块内存区域,被所有线程共享,在 JVM 启动时创建,主要用来存储 new 出来的对象。Java 中几乎所有的对象都会在堆中分配,堆也是垃圾收集器管理的目标区域。

qq_45801780的博客 1185

JVM 基础面经

程序计数器(Program Counter Register) 是 JVM 运行时内存结构中的一个重要组成部分,用于记录当前线程所执行的字节码的地址(即正在执行或即将执行的指令的地址)。它的作用类似于 CPU 的指令指针。1.线程私有:程序计数器是线程私有的,每个线程都有独立的一份,原因是 JVM 采用多线程运行,每条线程在执行字节码时需要独立追踪当前执行的位置。线程切换时,程序计数器可以帮助恢复到正确的执行位置,实现线程的正确调度。

weixin_51008312的博客 897

JVM面经

JVM主要组成部分 Class loader(类加载器):根据给定的全限定名类名来装载class文件到方法区 Execution engine(执行引擎):执行classes中的指令。 Native Interface(本地接口):与native libraries交互,是其它编程语言交互的接口。 Runtime data area(运行时数据区域):这就是我们常说的JVM的内存。 运行时数据区 程序计数器:当前线程所执行的字节码的行号指示器 2个作用 1记录线程执行位置; 2读取指令 Java 虚拟机栈

weixin_45326085的博客 1682
上一篇: JavaWeb(黑马程序员): 前端部分
下一篇: 黑马点评项目笔记
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值