JVM-类加载器、内存分配、垃圾回收

【001 Java语言和JVM虚拟机】

Java语言是一种先编译后解释的语言。JVM是在操作系统之上的虚拟处理器,工作流程主要包括加载class文件、管理并分配内存、执行垃圾收集。


【002 JVM类加载器的执行流程】

装载流程包括加载、链接和初始化,其中类加载器只负责加载流程。

加载:取得class文件的二进制流,并在堆空间创建class对象的引用。

链接:将二进制流合并到JVM虚拟机中,通过文件验证、准备(例如为static和final域设置初始化信息)、解析(例如将符号引用直接替换为地址引用),实现与平台相关。

初始化:执行类构造器的过程,如果没有通过new可以不执行。

【003 JVM类加载器的分类】

从上到下可以分为四类:BootStrap ClassLoader、External ClassLoad、App ClassLoad和Custom ClassLoad。BootStrap ClassLoader是类加载器的顶级,主要负责对rt.jar进行加载,加载的都是JVM运行的核心类库文件,如IO、Lang、JDBC;External ClassLoader主要是对jdk的其他扩展类库进行加载,如Resource、Swing、Security;App ClassLoader主要是对开发者提供的类进行加载,是最常见的加载器;Custom ClassLoader是要自定义的加载器,重新实现类的加载方法,如Tomcat就定义了加载器以改变加载双亲规则。

【004 JVM类加载模式】

JVM默认的加载模式是双亲加载,加载class文件时会从底层加载器开始检查,是否已用该类型加载器加载了class文件,如果已经加载,则返回已加载的类引用,如果没有加载,则会把加载权限交给上一级的加载器。如果在BootStrap ClassLoader中也没有找到加载的类,则尝试从顶层加载器开始加载该类,如加载不了,则交给下一级加载器进行加载。

双亲模式加载的好处是,避免了底层加载器修改上层加载器的加载规则,比较安全。

双亲模式加载的缺点是,子类加载器可以通过成员parent看到父类,而父类加载器看不到子类加载器的信息,如果存在例如JDBC这样的接口定义在jt.jar父类加载器,而具体实现类在App ClassLoader的情况,就会加载异常。需要通过在上下文中传入子类加载器来解决。

【005 Tomcat类加载模式】

Tomcat打破了双亲加载这一模式,自定义的WebAppClassLoader可以直接加载应用类,而不会先交给父类加载器。这样做的原因是因为一个JVM虚拟机里可以存在多个Web应用,双亲加载的模式会导致父类加载的类库会被多个应用共享,做不到应用隔离。同时,Tomcat的类加载器还提供了热部署的功能,不用重启就可以重新加载类库。

【006 自定义类加载器】

首先,要自定义一个继承了ClassLoader抽象类的子类,并为其指定父类加载器;其次,需要重写findClassLoader方法指定在何处获取已加载的类库和loadClass方法指定如何加载类库;最后,还需要通过应用程序显示制定类加载器(从这点上看,用new方法无法使用自定义的类加载器)。


【007 JVM的内存结构】

JVM内存可以分为方法区、栈区、堆区和本地方法区,此外还有寄存器和直接内存区域。其中,栈区和寄存器是归属于线程的,每个线程都有一份独立的空间;而其他空间是归属于进程的,所有线程共享。本地方法区可以直接调用内存。

【008 栈的内存分配】

栈区可以分为操作栈、局部变量栈等,用于保存线程运行的局部数据。另外,JVM还存在一种栈上分配的技术,如果启用DoEscapeAnalysis,那么被线程独享的小对象,如byte数组,可以被分配到栈空间,避免垃圾回收的消耗。

【008扩展如何使一个程序的函数调用尽可能深?】

一是扩大栈的内容空间,通过-Xss设置;二是减少一次调用使用的栈空间,方法包括减少局部变量的空间,少用long、double,减少参数个数,此外还可以关闭JVM栈上分配。

【009 堆的内存分配】

堆空间可以分为新生代、老年代和永久区。新生代通常占整个堆空间的3/8,可以分为edon、from和to三个子区域,比例分别为8:1:1,新创建的对象通常情况下会被分配到edon区域,在第一次垃圾回收后会被转移到其他区域。老年代存放的是存活时间较长的对象,或是对象的空间较大,在初始化创建或第一次垃圾回收时,就被直接放入。永久区严格意义上说,是方法区的实现,在JDK1.7后被移入堆内存,在JDK1.8后被移入元区。

【010 JVM内存异常】

内存异常,常见的两个错误是OutofMemoryException和StackOverFlowException。前一个异常在内存的所有区域都可能抛出,原因是空间内存不足或者空间泄漏。后一个异常出现在栈区,由于调用的深度超出限制,在实际的操作中,由于栈空间的大小和递归深度正相关,通常栈区更容易出现StackOverFlowException。

【011 JVM内存参数】

① 堆的分配参数:-Xmx和-Xms,最大堆空间和最小堆空间,一般设置为一样大小,否则在运行过程中JVM需要动态调整堆空间大小;-Xmn,新生代大小;-XX:NewRatio:设置新生代的比例;-XX:SurvivoRatio:设置edon:from的比例;-XX:OnOutOfMemoryError:内存溢出时可以指定脚本报错或重启JVM。

② 栈的内存分配:-Xss,通常根据内存的大小分配在几百K到1M之间。

③ 永久区分配:-XX:PermSize,-XX:MaxPErmSize,永久区的最小和最大空间。

【012 JVM内存模型】

JVM的共享内存,在被线程使用时,通常会拷贝一个副本放入线程工作区域,做到数据的互相隔离,当然这也会造成多个线程共享的数据不同步问题。对于Volatile变量,规定线程每次读取时必须从主内存中加载,而修改后必须同步到主内存中,保证数据的同步,但是加载(read,load)和更新(store,write)操作都不是原子性的操作,会造成线程不安全。对于Synchronized变量,是通过锁机制,保证数据在同一时间内只能被获得锁的线程访问,从而保证线程安全,但只有释放锁后才会把数据写回到主内存。对于final变量,虽然和普通变量一样使用,但由于其是不可修改的,因此也不存在同步问题。


【013 垃圾回收器算法】

GC是JVM的后台线程,只有在系统空闲并且内存区域不足时才会执行。

① 引用计数法,循环引用问题,Python

② 标记-清理算法,标记从root可达的所有对象,清除不可达对象,会造成零碎空间问题

③ 标记-压缩算法,标记从root可达的所有对象,移动到连续内存,然后清除其他所有空间

④ 复制算法,两个一样的内存块空间,把其中一块的可达对象移动到另一块连续存储,然后清除前一块的空间

【014扩展什么是root可达?】

从栈空间/方法区的Static成员/常量池/Native方法栈存在对象引用链,可以访问到对象。

【015 堆区采用的GC算法】

① 新生代的edon区域采用标记-清理算法,80%的新生代对象在第一次GC时就会被清理,标记-清理算法是最快的;

② 新生代的from和to区域采用复制算法,超过年龄限制的对象会被清理到老年代;

③ 老年代的GC并不是太频繁,采用标记-压缩算法,尽量避免零碎区域FULL GC。

【016 GC收集器实例】

① 串行回收器,暂停所有用户线程,新生代复制,老年代标记-压缩,适用于单CPU的应用;

② 并行回收器ParNew,暂停所有用户线程,新生代回收器,并行复制,适用于多核应用;

③ 并行回收器Parallel,暂停所有用户线程,新生代并行复制,老年代并行标记-压缩,可分开使用,更注重吞吐量;

④ 并发回收器CMS,是目前JVM虚拟机默认的GC算法,目标是最短时间回收,允许用户线程与回收线程并发执行,采用标记-清理算法,常用于老年代;CMS的回收分为初始标记、并发标记、重新标记、并发清理和并发重置,其中初始标记和重新标记是不允许并发的,并发重置是对产生的零碎空间进行压缩,但不是每次都要执行;

⑤ G1回收器,是JDK1.7之后提出的回收算法,把堆划分成若干个大小一致的区域,打破了新生代、老年代的物理隔离;标记算法同CMS,但是回收时会优先选择要回收区域最大的空间进行回收,保证可用的连续空间最大化。

【016扩展1 finalize和System.GC】

① Finalize是Object类的方法,会在第一次调用GC时被触发,对类进行清理,但也可以重写该方法复活对象,创建新的引用指向自身this;另外不推荐在该方法中进行资源的释放,因为该方法执行的时间不可控。

② System.GC是系统方法,可以通过显示调用通知JVM虚拟机需要进行垃圾回收,但并不能保证虚拟机的立即执行。

【016扩展2 STOP-THE-WORLD】

STOP-THE-WORLD是指从执行效果看,JVM虚拟机出现全局暂停的问题,GC是导致该现象的一个原因。这一现象对系统运行存在危害,例如主从备份的机器,主机发现系统暂停,会主动切换到备机执行,此时主机恢复运行,会出现不同步问题。

【016扩展3 JDK1.8的分代变化】

最大的变化是将永久区移到了元空间,其垃圾回收算法采用标记-压缩。该变化的原因是为了更好的控制类加载器和class对象的内存空间,因为永久区很少会执行GC,导致不用的类加载器和class对象不能被及时删除。


【017 JVM指令重排】

指令重排是指编译器或运行时环境,会根据指令的执行情况,优化各指令的执行顺序。指令重排对于一个线程的执行顺序没有影响,但是多个不同线程的执行中,会造成执行顺序变化,得到不同的执行结果。因此在指令执行过程中,需要通过各种锁机制保证执行顺序。

【018 JVM锁机制】

JVM锁与Java语言中的Lock和Synchronized对应的层次不同,是虚拟机执行的真正机制。JVM的锁机制更适用于竞争不频繁的场景,避免真正加锁,如果竞争激励,建议关闭JVM锁优化。

① 偏向锁,首次使用的线程会占有该锁,同一线程再次访问不受限制,如果其他线程需要访问,则偏向结束,需要进行Lock竞争;

② 轻量级锁,如果对象没有被锁定,则可以使用,如果存在竞争,则需要进行Lock竞争;

③ 自旋锁,如果需要的对象被锁定,可以执行一段空语句,再看是否还被占用;

④ 无锁CAS,check and swap,每次执行都检查对象是否被修改,被修改则进行修正。


【019 Java语法糖】

① 泛型与类型擦除:List经过编译后的实际类型为List,因此如果要真正限制集合类型,可以使用List,泛型擦除后的结果为List;

② 自动装箱和拆箱:包装类型Integer和基本类型int之间的互相比较,会默认转为int类型;Integer与Integer类型的==比较,[-128,127]区间范围内编译器会认为是int类型比较,否则就是对象地址比较;

③ foreach的遍历循环,编译时需要转化为下标方式,因此执行效率较低。

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

推荐阅读更多精彩内容

  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,488评论 3 83
  • Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制。JVM实...
    Rick617阅读 852评论 0 0
  • jvm原理 Java虚拟机是整个java平台的基石,是java技术实现硬件无关和操作系统无关的关键环节,是java...
    AI乔治阅读 17,190评论 21 486
  • 也许是空虚,也许是躁动. 寂寞的人不要在深夜里独自徘徊.
    黄橹阅读 279评论 0 0
  • 依稀记得小时候,夜晚,躺在院子里的椅子上,仰望天空,有一道隐隐约约的光亮,那是让牛郎织女相隔两岸的天河。 ...
    palmydog阅读 868评论 5 16