目录

  • 一、垃圾回收机制1、为什么需要垃圾回收2、垃圾回收发生在哪里3、对象在什么时候可以被回收 4、回收方法区5、Java中的引用类型
  • 二、垃圾回收算法1、分代收集理论2、标记-清除算法(Mark-Sweep)3、标记-复制算法(Copying)4、标记-整理算法(Mark-Compact)5、总结对比
  • 三、垃圾回收器1、Stop The World(STW)2、Serial 垃圾回收器3、Serial Old 垃圾回收器4、ParNew 垃圾回收器5、Parallel Scavenge 垃圾回收器6、Parallel Old 垃圾回收器7、CMS 垃圾回收器8、G1 垃圾回收器9、总结对比10、GC性能衡量指标
  • 四、内存设置和查看GC日志1、设置JVM内存2、查看GC日志
  • 五、内存分配与回收策略1、对象首先分配到Eden区2、Eden 区满了触发 YoungGC3、大对象将直接进入老年代4、长期存活的对象将进入老年代5、动态对象年龄判断6、无法放入Survivor区直接进入老年代7、老年代空间分配担保原则8、CMS触发OldGC9、G1垃圾回收10、触发GC的原因11、总结
  • 参考
  • 一、垃圾回收机制

1、为什么需要垃圾回收

Java 程序在虚拟机中运行,是会占用内存资源的,比如创建的对象、加载的类型数据等,而且内存资源都是有限的。当创建的对象不再被引用时,就需要被回收掉,释放内存资源,这个时候就会用到JVM的垃圾回收机制。

JVM 启动时就提供了一个垃圾回收线程来跟踪每一块分配出去的内存空间,并定期清理需要被回收的对象。Java 程序无法强制执行垃圾回收,我们可以通过调用 System.gc 方法来"建议"执行垃圾回收,但是否可执行,什么时候执行,是不可预期的。

2、垃圾回收发生在哪里

JVM内存模型中,程序计数器、虚拟机栈、本地方法栈这 3 个区域是线程私有的,随着线程的创建而创建,销毁而销毁。栈中的栈帧随着方法的调用而入栈,随着方法的退出而出栈,每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的。因此这三个区域的内存分配和回收都具有确定性。

而堆和方法区这两个区域则有着显著的不确定性:一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同条件分支所需要的内存也可能不一样,只有处于运行期间,才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的分配和回收是动态的。垃圾回收的重点就是关注堆和方法区中的内存,堆中的回收主要是垃圾对象的回收,方法区的回收主要是废弃常量和无用的类的回收。

3、对象在什么时候可以被回收

一般一个对象不再被引用,就代表该对象可以被回收。主流的虚拟机一般都是使用 可达性分析算法 来判断该对象是否可以被回收,有些内存管理系统也是用 引用计数法 来判断。

1)引用计数算法:

这种算法是通过在对象中添加一个引用计数器来判断该对象是否被引用了。每当对象被引用,计数器就加 1;每当引用失效,计数器就减 1。当对象的引用计数器的值为 0 时,就说明该对象不再被引用,可以被回收了。

引用计数算法实现简单,判断效率也很高,但它无法解决对象之间相互循环引用的问题。两个对象若互相引用,但没有任何其它对象引用他们,而它们的引用计数器都不为零,就无法被回收。

2)可达性分析算法:

GC Roots 是该算法的基础,GC Roots 是所有对象的根对象。在垃圾回收时,会从这些 GC Roots 根对象开始向下搜索,在搜索的这个引用链上的对象,就是可达的对象;而一个对象到 GC Roots 没有任何引用链相连时,就证明此对象是不可达的,可以被回收。

在Java中,可作为 GC Roots 对象的一般包括如下几种:

  • Java虚拟机栈中的引用的对象,如方法参数、局部变量、临时变量等
  • 方法区中的类静态属性引用的对象
  • 方法区中的常量引用的对象,如字符串常量池的引用
  • 本地方法栈中JNI的引用的对象
  • Java虚拟机内部的引用,如基本数据类型的 Class 对象,系统类加载器等

比如下面的代码:

其中,类静态变量 MAPPER,loadAccount 方法的局部变量 account1、account2、accountList 都可以作为 GC Roots(ArrayList 内部是用 Object[] elementData 数组来存放元素的)。

在调用 loadAccount 方法时,堆中的对象都是可达的,因为有 GC Roots 直接或间接引用到这些对象,此时若发生垃圾回收,这些对象是不可被回收的。loadAccount 执行完后,弹出栈帧,方法内的局部变量都被回收了,虽然堆中 ArrayList 对象还指向 elementData 数组,而 elementData 指向 Account 对象,但没有任何 GC Roots 的引用链能达到这些对象,因此这些对象将变为垃圾对象,被垃圾回收器回收掉。

4、回收方法区

方法区垃圾回收的“性价比”通常是比较低的,方法区的垃圾回收主要回收两部分内容:废弃的常量和不再使用的类型。

1)废弃的常量:

  • 如常量池中废弃的字面量,字段、方法的符号引用等

2)不再使用的类型:

判定一个类型是否属于“不再被使用的类”需要同时满足三个条件:

  • 该类所有的实例都已经被回收,Java堆中不存在该类及其任何派生子类的实例
  • 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

在大量使用反射、动态代理、CGLib等字节码框架,动态生成JSP以及OSGi这类频繁自定义类加载器的场景中,通常都需要Java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力。

5、Java中的引用类型

Java 中有四种不同的引用类型:强引用、软引用、弱引用、虚引用,这4种引用强度依次逐渐减弱。

1)强引用:

强引用是最普遍的引用方式,如在方法中定义:Object obj = new Object()。只要引用还在,垃圾回收器就不会回收被引用的对象。

2)软引用:

软引用是用来描述一些有用但非必须的对象,可以使用 SoftReference 类来实现软引用。对于软引用关联着的对象,在系统将要发生内存溢出异常之前(一般发生老年代GC时),会把这些对象列进回收范围之中。如果回收之后内存还是不足,才会报内存溢出的异常。

这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现内存缓存,当内存快满时,就回收掉这些软引用的对象,然后需要的时候再重新查询。比如下面的代码:

3)弱引用:

弱引用是用来描述非必须的对象,可以使用 WeakReference 类来实现弱引用。它只能生存到下一次垃圾回收发生之前(一般发生年轻代GC时),当垃圾回收机制开始时,无论是否会内存溢出,都将回收掉被弱引用关联的对象。

需注意的是,我们使用 SoftReference 来创建软引用对象,使用 WeakReference 来创建弱引用对象,垃圾回收时,是回收它们关联的对象,而不是 Reference 本身。同时,如果 Reference 关联的对象被其它 GC Roots 引用着,也是不能被回收的。如下面的代码,在垃圾回收时,只有 T002 这个 Account 对象能被回收,回收后 reference2.get() 返回值为 null,account、reference1、reference2 所指向的对象都不能被回收。

4)虚引用:

最没有存在感的一种引用关系,可以使用 PhantomReference 类来实现虚引用。存在不存在几乎没影响,也不能通过虚引用来获取一个对象实例,存在的唯一目的是被垃圾回收器回收后可以收到一条系统通知。

  • 回到顶部

二、垃圾回收算法

1、分代收集理论

大部分虚拟机的垃圾回收器都是遵循“分代收集”的理论进行设计的,它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般至少将堆划分为新生代和老年代两个区域,然后可以根据不同代的特点采取最适合的回收算法。在新生代中,每次垃圾回收时都有大量对象死去,因为程序创建的绝大部分对象的生命周期都很短,朝生夕灭。而新生代每次回收后存活的少量对象,将会逐步晋升到老年代中存放。老年代每次垃圾收集时只有少量对象需要被回收,因为老年代的大部分对象一般都是全局变量引用的,生命周期一般都比较长。

在Java堆划分出不同的区域之后,垃圾回收器就可以每次只回收其中某一个或者某些部分的区域,因而也有了“Young GC”、“Old GC”、“Full GC”这样的回收类型的划分。也能够针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾回收算法,因而发展出了“标记-复制算法”、“标记-清除算法”、“标记-整理算法”等针对性的垃圾回收算法。

GC类型:

  • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
  • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
  • 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
  • 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集,包括新生代、老年代、方法区的回收,一般 Full GC 等价于 Old GC。

经典分代模型:

2、标记-清除算法(Mark-Sweep)

标记-清除算法 分为“标记”和“清除”两个阶段,首先从 GC Roots 进行扫描,对存活的对象进行标记,标记完后,再统一回收所有未被标记的对象。

优点:

  • 标记-清除算法不需要进行对象的移动,只需回收未标记的垃圾对象,在存活对象比较多的情况下极为高效。

缺点:

  • 标记-清除算法执行效率不稳定,如果堆中对象很多,而且大部分都是要回收的对象,就必须要进行大量的标记和清除动作,导致标记、清除两个过程的效率随着对象数量增长而降低。
  • 标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

3、标记-复制算法(Copying)

标记-复制算法简称为复制算法,复制算法主要是为了解决标记-清除算法在存在大量可回收对象时执行效率低下和内存碎片的问题。

1)半区复制算法

它将可用内存划分为大小相等的两块,每次只使用其中的一块。当这一块的内存满了,就从 GC Roots 开始扫描,将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

优点:

  • 每次都是针对整个半区进行内存回收,清理速度快,没有内存碎片产生
  • 每次回收后,对象有序排列到另一个空闲区域,分配内存时也就不用考虑有空间碎片的复杂情况

缺点:

  • 如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销
  • 复制回收算法将可用内存缩小为了原来的一半,内存使用率低

2)复制算法的优化

大多数对象都是朝生夕灭,新生代中98%的对象几乎都熬不过第一轮回收,因此并不需要按照 1∶1 的比例来划分新生代的内存空间。

因此新生代复制算法一般是把新生代分为一块较大的 Eden 区和两块较小的 Survivor(from survivor、to survivor) 区,每次分配内存只使用 Eden 和其中一块 Survivor。发生垃圾回收时,将 Eden 和 Survivor 中仍然存活的对象一次性复制到另外一块 Survivor 空间上,然后直接清理掉 Eden 和已用过的那块 Survivor 空间,如此往复。当对象经过垃圾回收的次数超过一定阀值还未被回收掉时,就会进入老年代,有些大对象也可以直接进入老年代。

相比半区复制算法:

优点:HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8 : 1 : 1,新生代与老年代的比例大概是 1 : 2。内存空间利用率高,只会有 10% 的空闲空间。

缺点:有可能一次 Young GC 后存活的对象超过一个 survivor 区的大小,这时候会依赖其它内存区域进行分配担保,让这部分存活下来的对象直接进入另一个区域,一般就是老年代。

4、标记-整理算法(Mark-Compact)

复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,它不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

优点:没有内存碎片产生,适合老年代垃圾回收

缺点:会有对象的移动,老年代存活对象多,移动对象还需要更新指针,因此成本会更高

5、总结对比

  • 回到顶部

三、垃圾回收器

垃圾回收算法是内存回收的方法论,垃圾回收器是内存回收的实践者。不同的垃圾回收器有不同的特性,并没有一个万能或最好的垃圾回收器,只能根据不同的业务场景选择最合适的垃圾回收器,所以这节就来了解下各个垃圾回收器的特性。

1、Stop The World(STW)

先看看jvm的 “Stop The World” 问题。

1)STW:

可达性分析算法从 GC Roots 集合找引用链时,需要枚举根节点,然后从根节点标记存活的对象,根节点枚举以及整理内存碎片时,都会发生 Stop The World,此时 jvm 会直接暂停应用程序的所有用户线程,然后进行垃圾回收。因为垃圾回收时如果还在继续创建对象或更新对象引用,就会导致这些对象可能无法跟踪和回收、跟节点不断变化等比较复杂的问题,因此垃圾回收过程必须暂停所有用户线程,进入 STW 状态。垃圾回收完成后,jvm 会恢复应用程序的所有用户线程。

所有垃圾回收器都无法避免 STW,只能尽量缩短用户线程的停顿时间。系统停顿期间,无法处理任何请求,所有用户请求都会出现短暂的卡顿。如果因为内存分配不合理或垃圾回收器使用不合理,导致频繁的垃圾回收,而且每次回收系统停顿时间过长,这会让用户体验极差。jvm 最重要的一个优化就是通过合理的内存分配,使用合适的垃圾回收器,使得垃圾回收频率最小、停顿时间最短,避免影响系统正常运行。

2)安全点(Safe Point):

用户程序执行时并非在代码指令流的任意位置都能够停顿下来开始垃圾收集,而是强制要求必须执行到达安全点后才能够暂停。安全点可以理解成是在代码执行过程中的一些特殊位置,当线程执行到这些位置的时候,说明虚拟机当前的状态是安全的,如果有需要,可以在这个位置暂停。

安全点位置的选取基本上是以“是否具有让程序长时间执行的特征”为标准进行选定的,“长时间执行”的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点。

jvm 采用主动式中断的方式,在垃圾回收发生时让所有线程都跑到最近的安全点。主动式中断的思想是当垃圾回收需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志位,各个线程执行过程时会不停地主动去轮询这个标志,一旦发现中断标志为真时就自己在最近的安全点上主动中断挂起。

3)安全区域(Safe Region):

安全点机制保证了程序执行时,在不太长的时间内就会遇到可进入垃圾回收过程的安全点。但是,程序“不执行”的时候,线程就无法响应虚拟机的中断请求,如用户线程处于Sleep状态或者Blocked状态,这个时候就没法再走到安全的地方去中断挂起自己。这就需要安全区域来解决了。

安全区域是指能够确保在某一段代码片段之中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾回收都是安全的。当用户线程执行到安全区域里面的代码时,首先会标识自己已经进入了安全区域,那样当这段时间里虚拟机要发起垃圾回收时就不必去管这些已声明自己在安全区域内的线程了。当线程要离开安全区域时,它要检查虚拟机是否已经完成了需要暂停用户线程的阶段,如果完成了,那线程就继续执行;否则它就必须一直等待,直到收到可以离开安全区域的信号为止。

2、Serial 垃圾回收器

Serial 垃圾回收器是一个单线程回收器,它进行垃圾回收时,必须暂停其他所有用户线程,直到它回收结束。Serial 主要用于新生代垃圾回收,采用复制算法实现。

服务端程序几乎不会使用 Serial 回收器,服务端程序一般会分配较大的内存,可能几个G,如果使用 Serial 回收器,由于是单线程,标记、清理阶段就会花费很长的时间,就会导致系统较长时间的停顿。

Serial 一般用在客户端程序或占用内存较小的微服务,因为客户端程序一般分配的内存都比较小,可能几十兆或一两百兆,回收时的停顿时间是完全可以接受的。而且 Serial 是所有回收器里额外消耗内存最小的,也没有线程切换的开销,非常简单高效。

3、Serial Old 垃圾回收器

Serial Old 是 Serial 的老年代版本,它同样是一个单线程回收器,主要用于客户端程序。Serial Old 用于老年代垃圾回收,采用标记-整理算法实现。

Serial Old 也可以用在服务端程序,主要有两种用途:一种是与 Parallel Scavenge 回收器搭配使用,另外一种就是作为 CMS 回收器发生失败时的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。

4、ParNew 垃圾回收器

ParNew 回收器实质上是 Serial 回收器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为都与 Serial 回收完全一致,控制参数、回收算法、对象分配规则等都是一致的。除了 Serial 回收器外,目前只有 ParNew 回收器能与 CMS 回收器配合工作,ParNew 是激活CMS后的默认新生代回收器。

ParNew 默认开启的回收线程数与处理器核心数量相同,在处理器核心非常多的环境中,可以使用 -XX: ParallelGCThreads 参数来限制垃圾回收的线程数。

5、Parallel Scavenge 垃圾回收器

Parallel Scavenge是新生代回收器,采用复制算法实现,也是能够并行回收的多线程回收器。Parallel Scavenge 主要关注可控制的吞吐量,其它回收器的关注点是尽可能地缩短垃圾回收时的停顿时间。吞吐量就是处理器用于运行程序代码的时间与处理器总消耗时间的比值,总消耗时间等于运行程序代码的时间加上垃圾回收的时间。

Parallel Scavenge 提供了两个参数用于精确控制吞吐量:

  • -XX: MaxGCPauseMillis:控制最大垃圾回收停顿时间,参数值是一个大于0的毫秒数,回收器将尽力保证垃圾回收花费的时间不超过这个值。
  • -XX: GCTimeRatio:直接设置吞吐量大小,参数值是一个大于0小于100的整数,就是垃圾回收时间占总时间的比率。默认值为 99,即允许最大1%(即1/(1+99))的垃圾收集时间。

Parallel Scavenge 还有一个参数 -XX: +UseAdaptiveSizePolicy,当设置这个参数之后,就不需要人工指定新生代的大小、Eden与Survivor区的比例等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。

6、Parallel Old 垃圾回收器

Parallel Old 是 Parallel Scavenge 的老年代版本,支持多线程并发回收,采用标记-整理算法实现。在注重吞吐量或者处理器资源较为稀缺的场合,可以优先考虑 Parallel Scavenge 加 Parallel Old 这个组合。

7、CMS 垃圾回收器

CMS(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的回收器。CMS 用于老年代垃圾回收,采用标记-清除算法实现。

1)CMS 回收过程:

CMS 垃圾回收总体分为四个步骤:

  • 1)初始标记(会STW):初始标记需要 Stop The World,初始标记仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快。
  • 2)并发标记:并发标记阶段就是从 GC Roots 的直接关联对象开始遍历整个对象引用链的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾回收线程一起并发运行。
  • 3)重新标记(会STW):重新标记需要 Stop The World,重新标记阶段是为了修正并发标记期间,因程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。
  • 4)并发清除:清除阶段是清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发进行的。

最耗时的并发标记和并发清除阶段是和用户线程并发进行的,总体上来说,CMS 回收过程是与用户线程一起并发执行的,是一款并发低停顿的回收器。

2)触发CMS的条件

CMS GC 在实现上分成 foreground collector 和 background collector。

① foreground collector

foreground collector 触发条件比较简单,一般是遇到对象分配但空间不够,就会直接触发 GC,来立即进行空间回收。采用的算法是 mark sweep,不压缩。

② background collector

background collector 是通过 CMS 后台线程不断的去扫描,过程中主要是判断是否符合 background collector 的触发条件,一旦有符合的情况,就会进行一次 background 的 collect。每次扫描过程中,先等 CMSWaitDuration 时间(默认2秒),然后再判断是否满足 background collector 的触发条件。

background collector 的触发条件:

  • 并行 Full GC,如调用了 System.gc()
  • 未配置 UseCMSInitiatingOccupancyOnly 时,会根据统计数据动态判断是否需要进行一次 CMS GC。如果预测 CMS GC 完成所需要的时间大于预计的老年代将要填满的时间,则进行 GC。这些判断是需要基于历史的 CMS GC 统计指标,第一次 CMS GC

JVM性能调优(2)——垃圾回收器和回收策略相关推荐

  1. jvm性能调优 - 20G1垃圾回收器应该如何设置参数

    文章目录 Pre 什么时候触发新生代+老年代的混合垃圾回收? G1垃圾回收的过程 初始标记 并发标记 最终标记 混合回收 G1垃圾回收器的一些参数 回收失败时的Full GC 思考 Pre 上篇文章大 ...

  2. jvm性能调优 - 03垃圾回收机制

    文章目录 Pre 对象的分配与引用 一个方法执行完毕之后会怎么样? 不再需要的那些对象应该怎么处理?--GC 思考题 Pre 上一篇文章给大家分析了JVM中的几块内存区域分别都是干什么的,今天的文章就 ...

  3. JVM 调优和垃圾回收器说明

    转载自   JVM 调优和垃圾回收器说明 JVM垃圾收集算法 JVM垃圾收集算法有四种:标记-清除算法.复制算法.标记-整理算法.分代收集算法 标记-清除算法: 该算法如同它的名字一样,分为两个阶段: ...

  4. JVM性能调优监控工具专题二:VisualVM基本篇之监控JVM内存,CPU,线程

    2019独角兽企业重金招聘Python工程师标准>>> JVM性能调优监控工具专题二:VisualVM基本篇之监控JVM内存,CPU,线程 博客分类: java jvm 前言: 上一 ...

  5. JVM性能调优实践:G1 垃圾收集器介绍篇

    前言 前面两篇主要整理了性能测试的主要观察指标信息:性能测试篇,以及JVM性能调优的工具:JVM篇.这一篇先简单总结一下GC的种类,然后侧重总结下G1(Garbage-First)垃圾收集器的分代,结 ...

  6. Java虚拟机学习总结(4)—— JVM 性能调优常用参数详解

    前言 在JVM性能调优时有三个组件: 堆大小调整(内存最够大的话,尽量搞大点) 垃圾收集器调整 JIT 编译器(比较深,需要掌握源码才能更好的优化更加底层) 大多数调优选项都与调整堆大小和为您的情况选 ...

  7. JVM性能调优(4)——性能调优工具

    目录 一.JDK工具1.JDK工具2.利用 jps 找出进程3.利用 jstat 查看VM统计信息4.利用 jmap 查看对象分布情况5.利用 jstack 分析线程栈 二.Linux 命令行工具1. ...

  8. 深入理解Java虚拟机:Jvm性能调优

    本篇内容包括:Jvm 性能调优简介:根据需求目标进行 Jvm 调优规划(即 调优的目标.调优的步骤):Jvm 调优参数.命令.工具:以及 Java 中的内存泄露问题的详解- 一.Jvm 性能调优简介 ...

  9. JVM性能调优(一)

    作者 Eva Andreasson  译者:赵峰 校对:方腾飞  原文链接 Java应用程序是运行在JVM上的,但是你对JVM技术了解吗?这篇文章(这个系列的第一部分)讲述了经典Java虚拟机是怎么样 ...

  10. Mr. Cappuccino的第19杯咖啡——金三银四面试题之JVM性能调优篇

    金三银四面试题之JVM性能调优篇 1. 什么是Java类加载器? 2. 读取class文件的来源有哪些? 3. 谈谈类加载器加载的流程? 4. 类加载器有哪几种? 5. 哪些操作会初始化类加载器? 6 ...

最新文章

  1. Android之如何解决ScrollView起始位置不是最顶部的解决办法
  2. [转]用Whois获得电信运营商的IP地址是如何分配的?
  3. python 判断是否连接wifi_python操作 linux连接wifi,查看wifi连接状态方法
  4. Express简单路由使用
  5. 数字化工厂的五大系统_工厂通过MES系统对车间设备数字化管理
  6. 把酒言欢话聊天,基于Vue3.0+Tornado6.1+Redis发布订阅(pubsub)模式打造异步非阻塞(aioredis)实时(websocket)通信聊天系统
  7. 如何搭建用户体验指标体系?
  8. 【C++学习笔记2】构造函数、析构函数、函数多态、抽象类、函数模板
  9. Web前端工程师怎么样呢?薪资待遇如何呢?
  10. 解读CNAS更换徽标和认可标识
  11. python读json文件数组_如何在python中从json文件读取json对象数组
  12. 腾讯云互动直播SDK集成综述
  13. 【力扣】729. 我的日程安排表 I
  14. 第03篇 以太坊POA联盟链介绍
  15. 计算机教师资格教案,小学信息技术教师资格证面试教案:操作系统新相识
  16. dnf跨几服务器比较稳定,DNF极具特色的几个跨区,跨6知名度最高,这个跨区打团不用挤频道...
  17. CentOS 7虚拟机克隆,以及克隆后主机名和静态IP地址的修改,无法重启网络服务( because the control process exited with error code)的解决方法
  18. html5实现滚动文字
  19. 电脑变卡,电脑用久了会变卡怎么办?让电脑变得流畅方法图解
  20. Instagram 5位传奇工程师背后的技术揭秘

热门文章

  1. 单实例运行程序和SetEventData在非WinCE平台下的实现
  2. 【案例分享】百度超级链电子签约在电子商务场景的应用
  3. 黑马计划 | 硬核玩法 匠心设计,《天下为棋》制作人分享独游转手游“打怪”之路...
  4. date.timezone 设置默认地区时间
  5. 在Ubuntu 16.04上安装Intel 5300 CSI tool的全过程
  6. 42幅非常有创意的食品广告欣赏(下篇)
  7. Java第三天上课笔记
  8. java异常ppt_java异常和垃圾收集.ppt
  9. Contour Detection and Hierarchical Image Segmentation
  10. 有没有人会解析安装包重做游戏