面试题|Java|JVM

Jvm数据区

[图片上传失败...(image-cd876b-1594521117849)]

  • 线程共享区
    • 方法区(运行常量池)
      主要用于存放堆内存中的一些执行逻辑和变量,类的加载信息,常量,静态变量等一系列信息,和堆不同,方法去不需要频繁gc等操作,又叫永久代

    • Java中的类实例,数组都将存放在堆中,gc发生也是在堆中,堆是Jvm内存中最大的一块内存地址
  • 线程私有区
    • 虚拟机栈
      虚拟机栈就是线程所私有Java方法的内存区域,包括方法所引用的基本数据类型和引用。
      Java方法的使用就是压栈过程,基本数据类型和引用则是存在栈帧中,当方法抛出异常或结束则是出栈的操作。
    • 本地方法区
      native修饰的非Java方法
      本地方法栈也有自己的栈帧等,栈帧里面也相应的有局部变量表,操作数栈,动态链接,出口信息等
    • 程序计数器
      Jvm的本质是多线程交替的,但是如何保证每一个线程拿到cpu资源的时候可以从结束的地方接着开始,就需要程序技术器来保证

堆的分类

  • Eden
  • Survivor0
  • Survivor1
  • 老年代

首先分配内存到Eden区,当发生Gc之后转移到s0或者s1中(如果s0先被使用,那么下一次则使用s1,总之是为了保留新生代的内容),每一次gc都会给新生代数据记录次数,一般来说超过15次则进入老年代

运行常量池

  • 字符串内容
  • final修饰的关键字
  • 基本数据类型的值
  • 符号引用
    • 类和结构完全限定名
    • 字段名称和描述符
    • 方法名称和描述符

Java对象的创建过程

  1. 类加载
  2. 分配内存
  3. 初始化零值
  4. 设置对象头
  5. 执行init

类加载过程

类加载过程图
  1. 加载 (所需类的加载过程)

    • 通过类的全限定名获取该类的二进制流(通过class全限定名从本地,网络,专有数据库中的jar或者zip中获取.class文件)
    • 生成java.lang.class对象作为方法区进入该对象的入口
    • 将字节流的存储结构转化到jvm方法区中运行时的数据结构
  2. 验证 (了解Class字节文件是否符合当前虚拟机要求)

    • 文件格式验证: 字节流是否符合class文件规范
    • 元数据验证: 是否符合java的语法规范,例如继承接口,是否实现了接口中的方法
    • 字节码验证: 数据和控制流验证,保证方法中的类型转换有效
    • 符号引用验证: 验证是否可以通过符号引用找到相应的对象和变量
  3. 准备 (为类的变量分配内存和设置类的初始值(即方法区分配这些变量空间))

    • 为变量分配空间
    • 为变量初始化赋值的过程,例如int 赋值为 0 ,对象赋值为 null 等
    • 特殊 private static final a = 1
      正常来说,应该是初始化阶段赋值,但是这个情况下直接在方法去中替换a = 1 ,则在准备阶段就完成赋值
  4. 解析 (虚拟机将常量池中的符号引用转为直接引用的过程)

    • 什么是符号引用?
      用一串不会有歧义的符号来标识引用的对象或者是变量
    • 什么是直接引用?
      正式用指针去引用符号和变量
  5. 初始化

真正按照程序员的意愿去初始化值

分配内存

分配方法
  1. 指针碰撞
    内存空间比较整洁,直接移动指针分配空间
  2. 空闲链表
    “见缝插针”,空间不连续,则找到空闲的地方插入数据
线程安全问题
  1. 利用CAS不都安的尝试获取内存空间直到成功(目前虚拟机的解决方案)
  2. TLAB: 为每个线程独自分配一部分空间,且独有,分配的时候优先分配到该空间中

设置对象头

例如Synchronized关键字需要Mark Word中的monitor对象(MonitorExit,MonitorEnter)

对象的访问方式

  1. 句柄访问


    image

    reference -> 句柄池中指针 -> 实例数据

  2. 直接访问


    image

    reference -> 实例数据

  3. 句柄和直接访问的优缺点分析

论访问速度直接访问最快,但是如果需要删除的话需要直接删除数据
句柄中则可以直接把句柄值赋null,效率更快

对象死亡的分析方法

  1. 程序计数法 : 清零以后则可以判断死亡
  2. 可达性分析法 : 以Gc root为起点 看看各个对象是否可以连接起来,如果连接不起来,被独立则可以判断回收
  • 什么是Gc root(GC Roots一般在JVM的栈区域里产生)
    • 处于激活状态的线程
    • 栈中的对象
    • JNI中的变量
    • JNI的全局引用
    • 对象头的Monitor对象

引用的类型

  • 强引用 (大部分是强引用,虚拟机就算oom,也不会回收引用)
  • 软引用 (当内存不足的时候,会回收软引用)
  • 弱引用 (只要被回收器判定为垃圾,则直接回收,可以用于判断是否被gc回收)
  • 虚引用 (虚引用不在乎引用了什么对象,可以说是一种gc标志,必须配合引用队列,可以在对象被回收之前做一些操作)

垃圾回收算法

  • 标记清除算法(标记为垃圾后,直接删除)
  • 复制算法 (开辟两块空间,直接将为被回收的部分复制到另一部分的空间内)
  • 标记整理算法 (先标记,之后移动,使得内存空间干净)
  • 分代收集算法 (分为年轻代和老年代,分别使用不同的算法,新生代用复制算法, 老年代用标记整理算法)

垃圾回收器

  • Serial (单线程)

单线程回收垃圾,回收的时候需要暂停其它的一切线程

  • Parnew (多线程)

对Serial的升级,其它不变,就是变成多线程

  • Parallel Scavenge (多线程)

提升吞吐量(程序运行时间/CPU使用时间),提高回收次数,减少回收时间

  • CMS (真正意义上的多线程)
    • 标记清除算法
    • 过程
      1. 初始标记 (暂停所有线程,标记Gc Root相连的对象)
      2. 并发标记 (进行可达性分析,标记一系列回收对象)
      3. 重新标记 (修正并发标记期间用户的修改)
      4. 并发清除 (开始清理)
    • 缺点
      • 对CPU资源敏感
      • 无法清理浮动垃圾
      • 有太多的空间碎片
  • G1 (多线程,不区分新生代和老年代)
    • 预览图


      image
    • 分区 Region (G1对内存的使用以分区(Region)为单位,而对对象的分配则以卡片(Card)为单位。)

    不区分老年代和年轻代都会直接被划分成Region,每个Region还会被细分为若干个大小为512 Byte的Card,卡片还会记录在全局卡片表(Global Card Table)中.
    不同卡中的对象可能会互相引用,还可能是跨域Region引用,如果存在的话会直接写屏障(并发标记阶段)
    + 分区模型
    - 预览图

    image

    - 巨型对象 Humongous Region
    由于太大会导致分区出现问题,因此直接在老年代中分配空间
    - 已记忆集合 Remember Set (RSet) (存在于Region中)
    - 记录引用分区内对象的卡片索引
    - 内部使用Per Region Table (PRT) 记录使用引用的情况(稀少,细粒度,粗粒度)
    + 稀少 (记录到卡)
    + 细粒度 (记录到region)
    + 粗粒度 (记录到引用的数目)
    - 过程
    1. 初始标记 (GC Root的标记)
    2. 并发标记 (三色标记法开始)
    3. 最终标记 (三色标记的最终标记)
    4. 筛选回收 (复制清除算法)
    - 三色标记
    + white (不可达回收)
    + grey (子对象还未扫描完)
    + black (gc root)
    - STAB
    > 本质是上对对象的一次快照,快照的内容就是查看对象的颜色(三色标记)以维持并发的效率。

双清委派模型

采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类

双亲委派模型的破坏 (借鉴CSDN博主的文章说的很完整了)

原生的JDBC中Driver驱动本身只是一个接口,并没有具体的实现,具体的实现是由不同数据库类型去实现的。例如,MySQL的mysql-connector-.jar中的Driver类具体实现的。 原生的JDBC中的类是放在rt.jar包的,是由启动类加载器进行类加载的,在JDBC中的Driver类中需要动态去加载不同数据库类型的Driver类,而mysql-connector-.jar中的Driver类是用户自己写的代码,那启动类加载器肯定是不能进行加载的,既然是自己编写的代码,那就需要由应用程序启动类去进行类加载。于是乎,这个时候就引入线程上下文件类加载器(Thread Context ClassLoader)。有了这个东西之后,程序就可以把原本需要由启动类加载器进行加载的类,由应用程序类加载器去进行加载了。
————————————————
版权声明:本文为CSDN博主「Jack老师」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luoyang_java/article/details/92598142

参考文章

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354