24のJVM面質問と回答要約

1. Java仮想マシンとは何ですか?なぜJavaは、「プラットフォームに依存しないプログラミング言語」と呼ばれてきましたか?

Java仮想マシンは、仮想マシンがプロセスJavaバイトコードを実行することができます。Javaソースファイルは、Java仮想マシンで実行することができ、バイトコードファイルにコンパイルされています。Javaのはプログラマが個別に、または再コンパイル、各プラットフォームを書き換えるためのアプリケーションが必要とすることなく、任意のプラットフォーム上で実行できるように設計されています。Java仮想マシンは、基盤となるハードウェア・プラットフォームおよびその他の機能の命令長ということを知っているので、これは、可能になってみましょう。

2.Javaメモリ構造?

「24 JVM顔の質問と回答の要約」

Javaスタック、ネイティブメソッドスタックとプログラマはカウンタースレッドプライベートメモリ領域を実行している;そしてpは、メモリ領域のすべてのスレッドで共有メソッド領域であります

  • Javaヒープ(ヒープ)は、Java仮想マシン管理のメモリで最大です。仮想マシンの起動時にヒープメモリ領域内のすべてのスレッドで共有されているJavaが作成されます。このメモリ領域の唯一の目的は、ほぼすべてのオブジェクトインスタンスがメモリを割り当てるためにここにいる、オブジェクトのインスタンスを格納しています。
  • 地区方法(方法エリア)、メソッド領域(方法エリア)とJavaヒープ、各スレッドがストアクラス情報に使用される共有メモリ領域は、仮想マシンにロードされているように、一定の静的変数、リアルタイムコンパイラコードやその他のデータ。
  • プログラムカウンタ(プログラムカウンタレジスタ)、プログラムカウンタ(プログラムカウンタレジスタ)が小さいメモリスペースであり、その役割は、現在のスレッドによって実行される行番号指示子バイトコードとして見ることができます。
  • JVMスタック(JVMスタック)は、プログラムカウンタとして、Java仮想マシン・スタック(Java仮想マシンスタック)も、スレッドプライベート、そのライフサイクルと同じスレッドです。各メソッドが同時に実行される場合、ローカル変数テーブル、操作情報スタック、動的リンク、方法輸出を格納するためのスタックフレーム(スタック・フレーム)を作成する仮想マシンスタックのJavaメモリ・モデルを実行する方法が記載されています。各メソッドは、実行手順が完了するまで呼ばれ、スタックフレームはプロセススタックのスタックにスタックから仮想マシンに対応します。
  • ネイティブメソッドスタック(ネイティブメソッドスタック)、ネイティブメソッドスタック(ネイティブメソッドスタック)と仮想マシン(つまり、バイトコード)の役割は非常に似ているスタックが、その差は、Java仮想マシンのメソッドのための仮想マシンの実行スタックですサービス、およびローカル・スタック方式は、ネイティブメソッドのサービスに仮想マシンを使用することです。

3.使用上のスタックメモリ(スタック)、ヒープ(ヒープ)領域及び方法(メソッド領域)説明しました

通常、私たちはそこにあるオンサイトでの関数呼び出しの保存がJVMにおけるスタック領域を使用し、変数の基本データ型、オブジェクトへの参照を定義し、ヒープ上のスペースの新しいキーワードとコンストラクタによって作成されたオブジェクト、ヒープがありますガベージコレクタは、今世代のコレクションのアルゴリズムを使用しているため、ガベージコレクタによって管理されるメインエリア、ヒープスペースももう少し具体的にはエデン、サバイバーに分けることができ、旧世代と新世代に分けることができます(それがからに分けることができますそしてサバイバーのサバイバー)、終身、コード及び他のデータをコンパイルするための方法およびヒープ領域クラス情報を格納するための各共有メモリ領域のスレッドはJVMによってロードされてきており、定数、静的変数、JITコンパイラ、プログラムそのような直接書き込み100、「ハロー」として、(リテラル)リテラルおよび定数は、定数プールであり、プールは、方法の定常領域の一部です。スタック領域が不足し、最速が、スタックは通常、多数のオブジェクトをヒープ領域にあり、スタックとヒープサイズはJVM起動パラメータによって調整することができ、非常に小さいを動作させるためのスタック領域は、ヒープながら、にStackOverflowErrorにつながるとプールスペースの一定の欠如はOutOfMemoryErrorが発生するでしょう。

String str = new String("hello"); 

スタック上に上記のステートメントのstr変数は、リテラルの方法は、エリアにある新しい文字列ヒープ上のオブジェクト、および「ハロー」とそれを作成します。

補足1:Javaの新しいバージョン(Javaのアップデート6から開始)、なぜなら成熟した開発JITコンパイラの割り当てと「エスケープ分析」テクノロジー・スタック、スカラー交換や他の最適化技術の目的はで割り当てる必要がありますようにヒープこの事は少なく、カテゴリとなっています。

補足2:実行時定数プールは、クラスファイルの定数プールに相当する動的で、Java言語のみ動作中にコンパイルされた一定の期間を生成するために、定数を必要としないにもプール、インターンStringクラス()メソッドへの新しい定数とすることができますそれは、このようなものです。Javaの7の結果であり、どのような次のコードの実行を検討し、操作の結果の前後で比較すると同じです。

String s1 = new StringBuilder("go") .append("od").toString(); System.out.println(s1.intern() == s1); String s2 = new StringBuilder("ja") .append("va").toString(); System.out.println(s2.intern() == s2); 

4.オブジェクト割り当てルール

  • マイナーGCを実行するための十分なスペース、仮想マシンが存在しない場合にはエデン、エデンゾーンの割り当て領域に優先順位をオブジェクト。
  • 直接歳にラージオブジェクト(ラージオブジェクトがオブジェクトである連続した大量のメモリ空間を必要とします)。この目的は、メモリコピー領域二つエデンサバイバー領域との間に生じる(新世代のメモリを収集するために用いられるコピーアルゴリズム)の多数を回避することです。
  • 古い時代にオブジェクトの長期生存。年齢・カウンタの各オブジェクトの仮想マシン定義、1マイナーGC後のオブジェクトは、オブジェクトがサバイバーの領域に入る場合は、マイナーGCを介して各時間後、被験者の年齢プラス1した後、オブジェクトは古い時代領域にしきい値に達する知っています。
  • 動的オブジェクトの年齢を決定します。半分のスペースと同じ年齢サバイバー地域サバイバー以上のすべてのオブジェクトのサイズの合計は、に等しいか、または対象の年齢よりも古い場合は、直接歳にアクセスすることができます。
  • スペースの割り当てを保証。たびマイナーGCは、JVMは、この値はチェックが少ないセットHandlePromotionFailureを超える場合、残りの領域のサイズは、全GC熟成さよりも大きい場合、それはサバイバー高齢者エリアに移動する対象物の平均サイズを計算します、唯一のモニターであれば真でありますGCは、偽の場合は[フルGCを実施しました。

ロードされたクラスは何5.

メモリにランタイムデータ領域領域内のメソッドを、クラスの.classファイルを読み取るバイナリデータをロードするクラスを参照し、その後のjava.lang.Classのためのヒープ領域にオブジェクトを作成カプセル化クラスのデータ構造領域の方法。最終製品がロードされたクラスのクラスオブジェクトをスタック領域に位置し、クラスメソッド領域にカプセル化データ構造クラスのオブジェクト、Javaプログラマに領域のデータ構造のためのインタフェースアクセス方法を提供します。

6.クラスローダ

「24 JVM顔の質問と回答の要約」

  • 启动类加载器:Bootstrap ClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库
  • 扩展类加载器:Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载DK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
  • 应用程序类加载器:Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器

7.描述一下JVM加载class文件的原理机制?

答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。 由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。 类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:

  • Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
  • Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
  • System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。

8.描述一下JVM加载class文件的原理机制?

JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:

  • 1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;
  • 2)如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。

从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:

  • Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
  • Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
  • System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。

9.Java对象创建过程

1.JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用。然后加载这个类(类加载过程在后边讲)

2.为对象分配内存。一种办法“指针碰撞”、一种办法“空闲列表”,最终常用的办法“本地线程缓冲分配(TLAB)”

3.将除对象头外的对象内存空间初始化为0

4.对对象头进行必要设置

10.类的生命周期

类的生命周期包括这几个部分,加载、连接、初始化、使用和卸载,其中前三部是类的加载的过程,如下图; 「24 JVM顔の質問と回答の要約」

  • 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象
  • 连接,连接又包含三块内容:验证、准备、初始化。 1)验证,文件格式、元数据、字节码、符号引用验证; 2)准备,为类的静态变量分配内存,并将其初始化为默认值; 3)解析,把类中的符号引用转换为直接引用
  • 初始化,为类的静态变量赋予正确的初始值
  • 使用,new出对象程序中使用
  • 卸载,执行垃圾回收

11.Java对象结构

Java对象由三个部分组成:对象头、实例数据、对齐填充。

对象头由两部分组成,第一部分存储对象自身的运行时数据:哈希码、GC分代年龄、锁标识状态、线程持有的锁、偏向线程ID(一般占32/64 bit)。第二部分是指针类型,指向对象的类元数据类型(即对象代表哪个类)。如果是数组对象,则对象头中还有一部分用来记录数组长度。

实例数据用来存储对象真正的有效信息(包括父类继承下来的和自己定义的)

对齐填充:JVM要求对象起始地址必须是8字节的整数倍(8字节对齐)

12.Java对象的定位方式

句柄池、直接指针。

13.如何判断对象可以被回收?

判断对象是否存活一般有两种方式:

  • 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
  • 可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,不可达对象。

14.JVM的永久代中会发生垃圾回收么?

垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。请参考下Java8:从永久代到元数据区 (注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)

15.引用的分类

  • 强引用:GC时不会被回收
  • 软引用:描述有用但不是必须的对象,在发生内存溢出异常之前被回收
  • 弱引用:描述有用但不是必须的对象,在下一次GC时被回收
  • 虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用PhantomReference实现虚引用,虚引用用来在GC时返回一个通知。

###GC是什么?为什么要有GC? 答:GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。 垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验,其中一个深层次的原因就在于android系统中垃圾回收的不可预知性。

补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,但是Java对其进行了改进,采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域:

  • 伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。
  • 幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
  • 终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。 与垃圾回收相关的JVM参数:

-Xms / -Xmx — 堆的初始大小 / 堆的最大大小 -Xmn — 堆中年轻代的大小 -XX:-DisableExplicitGC — 让System.gc()不产生任何作用 -XX:+PrintGCDetails — 打印GC的细节 -XX:+PrintGCDateStamps — 打印GC操作的时间戳 -XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小 -XX:NewRatio — 可以设置老生代和新生代的比例 -XX:PrintTenuringDistribution — 设置每次新生代GC后输出幸存者乐园中对象年龄的分布 -XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老年代阀值的初始值和最大值 -XX:TargetSurvivorRatio:设置幸存区的目标使用率

16.判断一个对象应该被回收

1.该对象没有与GC Roots相连

2.该对象没有重写finalize()方法或finalize()已经被执行过则直接回收(第一次标记)、否则将对象加入到F-Queue队列中(优先级很低的队列)在这里finalize()方法被执行,之后进行第二次标记,如果对象仍然应该被GC则GC,否则移除队列。 (在finalize方法中,对象很可能和其他 GC Roots中的某一个对象建立了关联,finalize方法只会被调用一次,且不推荐使用finalize方法)

17.回收方法区

方法区回收价值很低,主要回收废弃的常量和无用的类。

如何判断无用的类:

1.该类所有实例都被回收(Java堆中没有该类的对象)

2.加载该类的ClassLoader已经被回收

3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方利用反射访问该类

18.垃圾收集算法

GC最基础的算法有三种: 标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。

  • 标记 -清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
  • 复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
  • 标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
  • 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

19.垃圾回收器

  • Serial收集器,串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。
  • ParNew收集器,ParNew收集器其实就是Serial收集器的多线程版本。
  • Parallel收集器,Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。
  • Parallel Old 收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法
  • CMS收集器,CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
  • G1收集器,G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征

20.GC日志分析

摘录GC日志一部分(前部分为年轻代gc回收;后部分为full gc回收):

2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs] 2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs] 

通过上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen属于Parallel收集器。其中PSYoungGen表示gc回收前后年轻代的内存变化;ParOldGen表示gc回收前后老年代的内存变化;PSPermGen表示gc回收前后永久区的内存变化。young gc 主要是针对年轻代进行内存回收比较频繁,耗时短;full gc 会对整个堆内存进行回城,耗时长,因此一般尽量减少full gc的次数

21.调优命令

Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo

  • jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
  • jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
  • jmap,JVM Memory Map命令用于生成heap dump文件
  • jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
  • jstack,用于生成java虚拟机当前时刻的线程快照。
  • jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。

22.调优工具

常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

  • JConsoleは、Javaは監視および管理コンソールは、メモリ、スレッドを監視するためのJDK Java監視および管理コンソールに来て最初からjava5と、JVMクラス、などであります
  • jvisualvmは、スナップショット・スレッドをメモリのスナップショットを分析することができる独自の多目的ツールをjdk、メモリの変更、GCの変化を監視します。
  • MAT、メモリアナライザツール、Eclipseベースのメモリ分析ツールは、メモリリークを見つける私たちを助けるとメモリ消費量を減らすことができ、高速、機能豊富なJavaヒープ解析ツールです
  • GChisto、プロのGCログ解析ツール

23Minor GCとフルGC、それぞれ、ときに起こるのですか?

MGCはまたYGC呼ば発生したときのメモリの新世代が十分でない場合には、JVMが十分でないメモリは、FGCを発生します

24.あなたはどのようなJVMのチューニングを知っていますか

  • セットヒープメモリサイズ

-Xmx:最大ヒープメモリ制限。

  • 新世代のサイズを設定します。新世代は、それ以外の場合は、古い年のオブジェクトの流入が多数存在します、小さすぎてはなりません

-XX:NewSizeの:新世代のサイズ

-XX:NewRatio新世代と旧世代が占め

-XX:SurvivorRatio:会計エデン空間と生存スペース

  • ガベージコレクタを設定する若い代替-XX:+ UseParNewGC古い代替-XX:+ UseConcMarkSweepGCを

おすすめ

転載: www.cnblogs.com/mxb0611/p/12081812.html