当前位置:网站首页 > 欧洲联赛 > 正文

梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神

admin 0

描绘一下 JVM 的内存区域

  • 程序计数器(PC,Program Counter Register)。在 JVM 标准中,每个线程都有它自己的程序计数器,并且任何时刻一个线程都只需一个办法在履行,也便是所谓的当时办法。程序计数器会存储当时线程正在履行的 Java 办法的 JVM 指令地址;或许,假如是在履行本地办法,则是未指定值(undefined)。
  • Java 虚拟机栈(Java Virtual Machine Stack),前期也叫 Java 栈。每个线程在创立时都会创立一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 办法调用。前面谈程序计数器时,说到了当时办法;同理,在一个时刻点,对应的只会有一个活动的栈帧,一般叫作当时帧,办法地点的类叫作当时类。假如在该办法中调用了其他办法,对应的新的栈帧梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神会被创立出来,成为新的当时帧,一向到它回来成果或许履行完毕。JVM 直接对 Java 栈的操作只需两个,便是对栈帧的压栈和出栈。栈帧中存储着局部变量表、操作数(operand)栈、动态链接、办法正常退出或许反常退出的界说等。
  • 堆(Heap),它是 Java 内存办理的中心区域,用来放置 Java 方针实例,简直一切创立的Java 方针实例都是被直接分配在堆上。堆被一切的线程同享,在虚拟机发动时,咱们指定的“Xmx”之类参数便是用来指定最大堆空间等方针。天经地义,堆也是废物搜集器要点照料的区域,所以堆内空间还会被不同的废物搜集器进行进一步的细分,最有名的便是新生代、老时代的区分。
  • 办法区(Method Area)。这也是一切线程同享的一块内存区域,用于存储所谓的元(Meta)数据,例如类结构信息,以及对应的运转时常量池、字段、办法代码等。由于前期的 Hotspot JVM 完结,许多人习惯于将办法区称为永久代(Permanent Generation)。Oracle JDK 8 中将永久代移除,一起添加了元数据区(Metaspace)。
  • 运转时常量池(Run-Time Constant Pool),这是办法区的一部分。假如仔细剖析过反编译的类文件结构,你能看到版别号、字段、办法、超类、接口等各种信息,还有一项信息便是常量池。Java 的常量池能够寄存各种常量信息,不论是编译期生成的各种字面量,仍是需求在运转时决议的符号引证,所以它比一般言语的符号表存储的信息愈加广泛。
  • 本地办法栈(Native Method Stack)。它和 Java 虚拟机栈是十分相似的,支撑对本地办法的调用,也是每个线程都会创立一个。在 Oracle Hotspot JVM 中,本地办法栈和 Java 虚拟机栈是在同一块儿区域,这彻底取决于技能完结的决议,并未在标准中强制。

形成OOM的原因有哪几种?

  • 堆内存不足是最常见的 OOM 原因之一,抛出的错误信息是“java.lang.OutOfMemoryError:Java heap space”,原因或许千奇百怪,例如,或许存在内存走漏问题;也很有或许便是堆的巨细不合理,比方咱们要处理比较可观的数据量,可是没有显式指定 JVM 堆巨细或许指定数值偏小;或许呈现 JVM 处理引证不及时,导致堆积起腾奥牌工业吸尘器来,内存无法开释等。
  • 虚拟机栈和本地办法栈,这儿要略微杂乱一点。假如咱们写一段程序不断的进行递归调用,并且没有退出条件,就会导致不断地进行压栈。相似这种状况,JVM 实际会抛出StackOverFlowError;当然,假如 JVM 企图去扩展栈空间的的时分失利,则会抛出OutOfMemoryError。
  • 关于老版别的 Oracle JDK,由于永久代的巨细是有限的,并且 JVM 对永久代废物收回(如,常量池收回、卸载不再需求的类型借种2)十分不活跃,所以当咱们不断添加新类型的时分,永久代呈现OutOfMemoryError 也十分多见,尤其是在运转时存在许多动态类型生成的嗜血角斗士场合;相似 Intern 字符串缓存占用太多空间,也会导致 OOM 问题。对应的反常信息,会符号出来和永久代相关:“java.lang.OutOfMemoryError: PermGenspace

GC 算法

  • 仿制(Copying)算法,我前面讲到的新生代 GC,底子都是根据仿制算法,将活着的方针仿制到 to 区域,仿制进程中将方针次序放置,就能够防止内存碎片化。这么做的价值是,已然要进行仿制,既要提早预留内存空间,有必定的糟蹋;冰恋秀色别的,关于 G1 这种分拆成为许多 region 的 GC,仿制而不是移动,意味着 GC 需求保护 region 之间方针引证联系,这个开支也不小,不论是内存占用或许时刻开支。
  • 符号 - 铲除(Mark-Sweep)算法,首要进行符号作业,标识出一切要收回的方针,然后进行铲除。这么做除了符号、铲除进程功率有限,别的便是不可防止的呈现碎片化问题,这就导致其不适合特别大的堆;不然,一旦呈现 Full GC,暂停时刻或许底子无法承受。
  • 符号 - 收拾(Mark-Compact),相似于符号 - 铲除,但为防止内存碎片化,它会在收拾进程中将方针移动,以确保移动后的方针占用接连的内存空间。

G1 废物梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神收回器选用的是什么废物收回算法?

从 GC 算法的视点,G1 挑选的是复合算法,能够简化了解为:

  • 在新生代,G1 选用的仍然是并行的仿制算法,所以同样会发作 Stop-The-World 的暂停。
  • 在老时代,大部分状况下都是并发符号,而收拾(Compact)则是和新生代 GC 时捎带进行,并且不是整体性的收拾,而是增量进行的。

GC 调优思路

从功用的视点看,一般重视三个方面,内存占用(footprint)、延时(latency)和吞吐量(throughput),大多数状况下调优会侧重于其间一个或许两个方面的方针,很少有状况能够统筹三个不同的视点。当然,除了上面一般的三个方面,也或许需求考虑其他 GC 相关的场景,例如,OOM 也或许与不合理的 GC 相关参数有关;或许,运用发动速度方面的需求,GC 也会是个考虑的方面。 底子的调优思路能够总结为:

  • 了解运用需求和问题,确认调优方针。假定,咱们开发了一个运用效劳,但发现偶然会呈现功用颤动,呈现较长毛球祖玛的效劳中止。评价用户可承受的呼应时刻和业务量,将方针简化为,期望 GC 暂停尽量控制在 200ms 以内,并且确保必定标准的吞吐量。
  • 把握 JVM 和 GC 的状况,定位详细的问题,确认真的有 GC 调优李天煜的必要。详细有许多办法,比方,经过 老公手淫jstat 等东西检查 GC 等相关状况,能够敞开 GC 日志,或许是运用操作体系供给的确诊东西等。例如,经过追寻 GC 日志,就能够查找是不是 GC 在特定时刻发作了长时刻的暂停,从而导致了运用呼应不及时。
  • 挑选的 GC 类型是否契合咱们的运用特征,假如是,详细问题表现在哪里,是 Minor GC 过长,仍是 Mixed GC 等呈现反常中止状况;假如不是,考虑切霍泊宏换到什么类型,如 CMS 和 G1 都是更侧重于低推迟的 GC 选项。

经过剖析确认详细调整的参数或许软硬件装备。验证是否到达调优方针,假如到达方针,即能够考虑完毕调优;不然,重复完结剖析、调整、验证这 个进程。

怎样进步JVM的功用?

  1. 新方针预留在年青代 经过设置一个较大的年青代预留新方针,设置合理的 Survivor 区并且供给 Survivor 区的运用率,能够将年青方针保存在年青代。权色床榻1
  2. 大方针进入年迈代 运用参数-XX:PetenureSizeThreshold 设置大方针直接进入梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神年迈代的阈值
  3. 设置方针进入年迈代的年纪 这个阈值的最大值能够经过参数-XX:MaxTenuringThreshold 来设置,默许值是 15
  4. 安稳的 Java 堆 取得一个安稳的堆巨细的办法是使-Xms 和-Xmx 的巨细共同,即最大堆和最小堆 (初始堆) 相同。
  5. 增大吞吐量提高体系功用 –Xmx380m –Xms3800m:设置 Java 堆的最大值和初始值。一般状况下,为了防止堆内存的频频震动,导致体系功用下降,咱们的做法是设置最大堆等于最小堆。假定这儿把最小堆削减为最大堆的一半,即 1900m,那么 JVM 会尽或许在 1900MB 堆空间中运转,假如这样,发作 GC 的或许性就会比较高; -Xss128k:削减线程栈的巨细,这样能够使剩下的体系内黄总韩燕存支撑更多的线程; -Xmn2g:设置年青代区域巨细为 2GB; –XX:+UseParallelGC:年青代运用并行废物收收回集器。这是一个重视吞吐量的搜集器,能够尽或许地削减 GC 时刻。 –XX:ParallelGC-Threads:设置用于废物收回的线程数,一般状况下,能够设置和 CPU 数量持平。但在 CPU 数梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神量比较多的状况下,设置相对较小的数值也是合理的; –XX:+UseParallelOldGC:设置年迈代运用并行收收回集器。
  6. 测验运用大的内存分页 –XX:+LargePageSizeInBytes:设置大页的巨细。 内存分页 (Paging) 是在运用 MMU 的根底上,提出的一种内存办理机制。它将虚拟地址和物理地址按固定巨细(4K)分割成页 (page) 和页帧 (page frame),并确保页与页帧的巨细相同。这种机制,从数据结构上,确保了拜访内存的高效,并使 OS 能支撑非接连性的内存分配。
  7. 运用非占有的废物收回器 为下降运用软件的废物收回时的中止,首要考虑的是运用重视体系中止的 CMS 收回器,其次,为了削减 Full GC 次数,应尽或许将方针预留在年青代。

system.gc() 的作用是什么?

gc()函数的作用仅仅提示虚拟机:程序员期望进行一次废物收回。可是它不能确保废物收回必定会进行,并且详细什么时分进行是取决于详细的虚拟机的,不同的虚拟机有不同的对策。

Parallel GC、CMS GC、ZGC、Azul Pauseless GC最首要的不同是?背面的原理也请简略描绘下?

Parallel GC的Young区选用的是Mark-Copy算法,Old区选用的是Mark-Sweep-Compact来完结,Parallel履行,所以决议了Parallel GC在履行YGC、FGC时都会Stop-The-World,但完结GC的速度也会比较快。 CMS GC的Young区选用的也是Mark-Copy,Old区选用的是Concurrent Mark-Sweep,所以决议了CMS GC在对old区收回时形成的STW时刻会更短,防止对运用发生太大的时延影响。 G1 GC选用了Garbage First算法,比较杂乱,完结的好呢,理论上是会比CMS GC能够更高效,一起对运用的影响也很小。 ZGC、Azul Pauseless GC选用的算法很不相同,尤其是Pauseless GC,其间的很重要的一个技巧是经过添加Read Barrier来更好的识别对GC而言最要害的references改变的状况。

什么时分履行ygc,fullgc?

当young gen中的eden区分配满的时分触发young gc,当年迈代内存不足时,将履行Major GC,也叫 Full GC。

gc()函数的作用仅仅提示虚拟机:程序员期望进行一次废物收回。可是它不能确保废物收回必定会进行,并且详细什么时分进行是取决于详细的虚拟机的,不同的虚拟机有不同的对策。

强引证、软引证、弱引证、幻象引证有什么差异?详细运用场景是什么?

不同的引证类型,首要表现的是方针不同的可达性(reachable)状况和对废物搜集的影响。

所谓强引证("Strong" Reference),便是咱们最常见的一般方针引证,只需还有强引证指向一个方针,就能标明方针还“活着”,废物搜集器不会碰这种方针。关于一个一般的方针,假如没有其他的引证联系,只需超过了引证的作用域或许显式地将相应(强)引证赋值为 null,便是能够被废物搜集的了,当然详细收回机遇仍是要看废物搜集战略。

软引证(SoftReference),是一种相对强引证弱化一些的引证,能够让方针豁免一些废物搜集,只需当 JVM 以为内存不足时,才会去企图收回软引证指向的方针。JVM 会确保在抛出OutOfMemoryError 之前,收拾软引证指向的方针。软引证一般用来完结内存灵敏的缓存,假如还有闲暇内存,就能够暂时保存缓存,当内存不足时收拾掉,这样就确保了运用缓存的一起,不会耗尽内存。

SoftReference 在“弱引证WeakReference”中归于最强的引证。SoftReference 所指向的方针,当没有强引证指向它时,会在内存中逗留一段的时刻,废物收回器会根据 JVM 内存的运用状况(内存的紧缺程度)以及 SoftReference 的 get() 办法的调用状况来决议是否对其进行收回。

关于幻象引证(PhantomReference ),有时分也翻译成虚引证,你不能经过它拜访方针。幻象引证仅仅是供给了一种确保方针被 finalize 今后,做某些工作的机制,比方,一般用来做所谓的 Post-Mortem 收拾机制,如 Java 渠道自身 Cleaner 机制等,也有人运用幻象引证监控方针的创立和毁掉。

Object counter = new Object();
ReferenceQueue refQueue = new ReferenceQueue<>();
PhantomReference

p = new PhantomReference<>(counter, refQueue);
counter = null;
Syst清朝明月光em.gc();
try {
// Remove 是一个堵塞办法,能够指定 timeout,或许挑选一向堵塞
Reference

ref = refQueue.remove(1000L);
if (ref != null) {
// do something
}
} catch (InterruptedException e) {
// Handle it
}

JVM类加载进程

一般来说密桃社,咱们把 Java 的类加载进程分为三个首要进程:加载、链接、初始化。 首要是加载阶段(Loading),它是 Java 将字节码数据从不同的数据源读取到 JVM 中,并映射为 JVM 认可的数据结构(Class 方针),这儿的数据源或许是各式各样的形状,如 jar 文件、class 文件,乃至是网络数据源等;假如输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。加载阶段是用户参加的阶段,咱们能够自界说类加载器,去完结自己的类加载进程。

第二阶段是链接(Linking),这是中心的进程,简略说是把原始的类界说信息滑润地转化入 JVM 运转的进程中。这儿可进一步细分为三个进程:

  • 验证(Verification),这是虚拟机安全的重要保障,JVM 需求核验字节信息是契合 Java 虚拟机标准的,不然就被以为是 VerifyError,这样就防止了歹意信息或许不合规的信息损害 JVM 的运转,验证阶段有或许触发更多 class 的加载。
  • 预备(Preparation),创立类或接口中的静态变量,并初始化静态变量的初始值。但这儿的“初始化”和下面的显式初始化阶段是有差异的,侧要点在于分配所需求的内存空间,不会去履行更进一步的 JVM 指令。
  • 解析(Resolution),在这一步会将常量池中的符号引证(symbolic reference)替换为直接引证。

最终是初始化阶段(initialization),这一步真实去履行类初始化的代码逻辑,包括静态字段赋值的动作,以及履行类界说中的静态初始化块内的逻辑,编译器在编译阶段就会把这部分逻辑收拾好,父类型的初始化逻辑优先于当时类型的逻辑。

什么是双亲派遣模型?

简略说便是当类加载器(Class-Loader)企图加载某个类型的时分,除非父加载器找不到相应类型,不然尽量将这个使命署理给当时加载器的父加载器去做。运用派遣模型的意图是防止重复加载 Java 类型。

类加载器的类型

  • 发动类加载器(Bootstrap Class-Loader),加载 jre/lib 下面的 jar 文件,如 rt.jar。它是个超级公民,即使是在敞开了 Security Manager 的时分,JDK 仍赋予了它加载的程序 AllPermission。
  • 扩展类加载器(Extension or Ext Class-Loader),担任加载咱们放到 jre/lib/ext/ 目录下面的 jar 包,这便是所谓的 extension 机制。该目录也能够经过设置 “java.ext.dirs”来掩盖。
  • 运用类加载器(Application or App Class-Loader),便是加载咱们最了解的 classpath

上下文类加载器

Java 供给了许多效劳供给者接口(Service Pr姚慧汶ovider Interface,SPI),答应第三方为这些接口供给完结。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 中心库来供给,而这些 SPI 的完结代码则是作为 Java 运用所依靠的 jar 包被包括进类途径(CLASSPATH)里。SPI接口中的代码常常需求加载详细的完结类。那么问题来了,SPI的接口是Java中心库的一部分,是由**发动类加载器(Bootstrap Classloader)来加载的;SPI的完结类是由燏怎样读体系类加载器(Sys梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神tem ClassLoader)**来加载的。引导类加载器是无法找到 SPI 的完结类的,由于按照双亲派遣模型,BootstrapClassloader无法派遣AppClassLoader来加载类。而线程上下文类加载器破坏了“双亲派遣模型”,能够在履行线程中扔掉双亲派遣加载链形式,使程序能够逆向运用类加载器。

ServiceLoader 的加载代码:

public static  ServiceLoader load(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}

ContextClassLoader默许寄存了AppClassLoader的引证,由于它是在运转时被放在了线程中,所以不论当时程序处于何处(BootstrapClassLoader或是ExtClassLoader等),在任何需求的时分都能够用Thread.currentThread().getContextClassLoader()取出运用程序类加载器来完结需求的操作。

自界说类加载器

自界说类加载器,常见的场景有:

  • 完结相似进程内阻隔,类加载器实际上用作不同的命名空间,以供给相似容器、模块化的作用。例如,两个模块依靠于某红花坂上的海个类库的不同版别,假如分别被不同的容器加载,就能够互不搅扰。这个方面的集大成者是Java EE和OSGI、JPMS等结构。
  • 运用需求从不同的数据源获取类界说信息,例如网络数据源,而不是本地文件体系。
  • 需求自己操作字节码,动态修正或许生成类型

从本地途径 load class 的比方:

public class CustomClassLoader extends ClassLoader {

@Override
public Class findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassFromFile(name);
return defineClass(name, b, 0, b.length);
}

private byte[] loadClassFromFile(String fileName) {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
fileName.replace('.', File.separatorChar) + ".class");
byte[] buffer;
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
int nextValue = 0;
try {
while ( (nextValue = inputStream.read()) != -1 ) {
byteStream.write(nextValue);
}
} catch (IOException e) {
e.printStackTrace();
}
buffer = byteStream.toByteArray();
return buffer;
}
}

动态署理的原理?

反射机制是 Java 言语供给的一种根底功用,赋予程序在运转时自省(introspect,官方用语)的才能。经过反射咱们能够直接操作类或许方针,比方获取某个方针的类界说,获取类声明的特点和办法,调用办法或许结构方针,乃至能够运转时修正类界说。 动态署理是一种便利运转时动态构建署理、动态处理署理办法调用的机制,许多场景都是运用相似机制做到的,比方用来包装 RPC 调用、面向切面的编程(AOP)。 完结动态署理的办法许多,比方 JDK 自身提赵布和供的动态署理,便是首要运用了上面说到的反射机制。还有其他的完结办法,比方运用传说中更高功用的字节码操作机制,相似 ASM、cglib(根据 ASM)、Javassist 等。

怎样运用JDK动态署理?

public class MyDynamicProxy {
public static void main (String[] args) {
HelloImpl hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
// 结构代码实例
Hello proxyHello = (Hello) Proxy.newProxyadn046Instance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler);
// 调用署理办法
proxyHello.sayHello();
}
}
interface Hello {
void sayHello();
}
class HelloImpl implements Hello {
@Override
public void sayHell重案追凶by百炼成猫o() {
System.out.println("Hello World");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Invoking sayHello");
Object result = method.invoke(target, args);
return result;
}
}

动态署理:JDK动态署理和CGLIB署理的差异?

JDK动态署理只梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神能对完结了接口的类生成署理,而不能针对类,CGLIB是针对类完结署理,首要是对指定的类生成一个子类,掩盖其间的办法(承继)。

JDK Proxy 的优势:

  1. 最小化依靠联系,削减依靠意味着简化开发和保护,JDK 自身的支撑,或许比 cglib 愈加牢靠。
  2. 滑润进行 JDK 版别晋级,而字节码类库一般需求进行更新以确保在新版 Java 上能够运用。
  3. 代码完结简略。

根据相似 cglib 结构的优势:

  1. 有的时分调用方针或许不方便完结额定接口,从某种视点看,约束调用者完结接口是有些侵入性的实践,相似 cglib 动态署理就没有这种约束。
  2. 只操作咱们关怀的类,而不用为其他相关类添加作业量。
  3. 高功用。

Spring在挑选用JDK仍是CGLiB的根据是什么?

(1)当Bean完结接口时,Spring就会用JDK的动态署理 (2)当Bean没有完结接口时,Spring运用CGlib是完结 (3)能够强制运用CGlib(在s梁咏琪,金三银四面试时节之Java 中心面试技能点 - JVM 小结,最强男神pring装备中参加

CGlib比JDK快?

(1)运用CGLib完结动态署理,CGLib底层选用ASM字节码生成结构,运用字节码技能生成署理类,比运用Java反射功率要高。仅有需求留意的是,CGL样本户之家ib不能对声明为final的办法进行署理,由于CGLib原理是动态生成被署理类的子类。可是JDK也在晋级,开端引进许多字节码技能来完结部分动态署理的功用,所以在某些测验下不用定是CGLib更快。

Java 中操作字节码的技能

ASM、Javassist、CGLib、Byte Budy。