zoukankan      html  css  js  c++  java
  • jvm面试题

    1、GC的回收流程是怎样的?

    对于整个的GC流程里面,那么最需要处理的就是新生代和老年代的内存清理操作,而元空间(永久代)都不在GC范围内。

    ①首先会判断Eden区是否有内存空间,如果此时有内存空间,则直接将新对象保存在伊甸园区。但是如果此时在伊甸园区内存不足,那么会自动执行一个Minor GC 操作,将伊甸园区的无用内存空间进行清理,Minor GC的清理范围只在Eden园区,清理之后会继续判断Eden园区的内存空间是否充足?如果内存空间充足,则将新对象直接在Eden园区进行空间分配。如果执行Minor GC 之后发现伊甸园区的内存空间依然不足,那么这个时候会执行存活区的判断,如果存活区有剩余空间,则将Eden园区部分活跃对象保存在存活区,那么随后继续判断Eden园区的内存空间是否充足,如果充足怎则将新对象直接在Eden园区进行空间分配。此时如果存活区没有内存空间,则继续判断老年区。则将部分存活对象保存在老年代,而后存活区将有空余空间。如果这个时候老年代也满了,那么这个时候将产生Major GC(Full GC),那么这个时候将进行老年代的清理;如果老年代执行Full GC之后,无法进行对象的保存,则会产生OOM异常,OutOfMemoryError异常

    2、JVM中类加载机制,类加载过程

    一般来说,我们把 Java 的类加载过程分为三个主要步骤:加载、链接、初始化。

    首先是加载阶段(Loading),它是 Java 将字节码数据从不同的数据源读取到 JVM 中,并映射为 JVM 认可的数据结构(Class 对象),这里的数据源可能是各种各样的形态,如 jar 文件、class 文件,甚至是网络数据源等;如果输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。加载阶段是用户参与的阶段,我们可以自定义类加载器,去实现自己的类加载过程。

    第二阶段是链接(Linking),这是核心的步骤,简单说是把原始的类定义信息平滑地转化入 JVM 运行的过程中。这里可进一步细分为三个步骤:

      验证(Verification),这是虚拟机安全的重要保障,JVM 需要核验字节信息是符合 Java 虚拟机规范的,否则就被认为是 VerifyError,这样就防止了恶意信息或者不合规的信息危害 JVM 的运行,验证阶段有可能触发更多 class 的加载。

      准备(Preparation),创建类或接口中的静态变量,并初始化静态变量的初始值。但这里的“初始化”和下面的显式初始化阶段是有区别的,侧重点在于分配所需要的内存空间,不会去执行更进一步的 JVM 指令。

      解析(Resolution),在这一步会将常量池中的符号引用(symbolic reference)替换为直接引用。在Java 虚拟机规范中,详细介绍了类、接口、方法和字段等各个方面的解析。

    最后是初始化阶段(initialization),这一步真正去执行类初始化的代码逻辑,包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑,编译器在编译阶段就会把这部分逻辑整理好,父类型的初始化逻辑优先于当前类型的逻辑。

    3、JVM内存调优

    1)监控GC的状态,使用各种JVM工具,查看当前日志,并且分析当前堆内存快照和gc日志,根据实际的情况看是否需要优化。
    2)通过JMX的MBean或者Java的jmap生成当前的Heap信息,并使用Visual VM或者Eclipse自带的Mat分析dump文件
    3)如果参数设置合理,没有超时日志,GC频率GC耗时都不高则没有GC优化的必要,如果GC时间超过1秒或者频繁GC,则必须优化
    4)调整GC类型和内存分配,使用1台和多台机器进行测试,进行性能的对比。再做修改,最后通过不断的试验和试错,分析并找到最合适的参数

    4、JVM内存区域的划分

    第一,程序计数器(PC,Program Counter Register)。在 JVM 规范中,每个线程都有它自己的程序计数器,并且任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的 Java 方法的 JVM 指令地址;或者,如果是在执行本地方法,则是未指定值(undefined)。(唯一不会抛出OutOfMemoryError)
    第二,Java 虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用。
    前面谈程序计数器时,提到了当前方法;同理,在一个时间点,对应的只会有一个活动的栈帧,通常叫作当前帧,方法所在的类叫作当前类。如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,成为新的当前帧,一直到它返回结果或者执行结束。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 虚拟机栈是在同一块儿区域,这完全取决于技术实现的决定,并未在规范中强制

  • 相关阅读:
    diary and html 文本颜色编辑,行距和其它编辑总汇
    bash coding to changeNames
    virtualbox ubuntu 网络连接 以及 连接 secureCRT
    linux 学习6 软件包安装
    linux 学习8 权限管理
    vim 使用2 转载 为了打开方便
    ubuntu
    linux 学习15 16 启动管理,备份和恢复
    linux 学习 14 日志管理
    linux 学习 13 系统管理
  • 原文地址:https://www.cnblogs.com/strong-FE/p/11976501.html
Copyright © 2011-2022 走看看