技术栈传送门
JAVA 基础手撸架构,Java基础面试100问_vincent-CSDN博客
JAVA 集合手撸架构,JAVA集合面试60问_vincent-CSDN博客
JVM 虚拟机手撸架构,JVM面试30问_vincent-CSDN博客
并发编程手撸架构,并发编程面试123问_vincent-CSDN博客
Spring 手撸架构,Spring面试63问_vincent-CSDN博客
Spring cloud 手撸架构,Spring cloud面试45问_vincent-CSDN博客
SpringBoot手撸面试,Spring Boot面试41问_vincent-CSDN博客
Netty 与 RPC手撸架构,Netty 与 RPC面试48问_vincent-CSDN博客
Doubo 手撸架构,Dubbo面试49问_vincent-CSDN博客
Redis手撸架构,Redis面试41问_vincent-CSDN博客
Zookeeper手撸架构,Zookeeper面试27问_vincent-CSDN博客
Mysql 手撸架构,Mysql 面试126问_vincent-CSDN博客
MyBatis手撸架构,MyBatis面试42问_vincent-CSDN博客
MongoDB 手撸架构,MongDB 面试50问_vincent-CSDN博客
Elasticsearch手撸架构,Elasticsearch 面试25问_vincent-CSDN博客
RabbitMQ 手撸架构,RabbitMQ 面试49问_vincent-CSDN博客
Kafka 手撸架构,Kafka 面试42问_vincent-CSDN博客
Docker手撸架构,Docker 面试25问_vincent-CSDN博客
Nginx手撸架构,Nginx 面试40问_vincent-CSDN博客
算法常用排序算法总结(1)-- 比较排序_vincent-CSDN博客_比较排序
常用排序算法总结(2)-- 非比较排序算法_vincent-CSDN博客_非比较排序的算法有
分布式事务分布式事务解决方案(总览)_vincent-CSDN博客
HTTP太厉害了,终于有人能把TCP/IP 协议讲的明明白白了_vincent-CSDN博客_tcp和ip

系统学习《深入了解JVM》https://blog.csdn.net/wuzhiwei549/category_9275772.html?spm=1001.2014.3001.5482https://blog.csdn.net/wuzhiwei549/category_9275772.html?spm=1001.2014.3001.5482

JDK、JRE、JVM的关系是什么?

什么是JvM?

英文名称( Java Virtual machine),就是JAA虚拟机,它只识别.cass类型文件,它能够将cass文件中的字节码指令进行识别并调用操作系统向上的A完成动作。

什么是JRE?

英文名称( Java runtime environment),Java运行时环境。它主要包含两个部分JVM的标准实现和Java的一些基本类库。相对于JVM来说,JRE多出来一部分Java类库。

什么是JDK?

英文名称( Java Development Kit),Java开发工具包。JDK是整个Java开发的核心,它集成了JRE和一些好用的小工具。例如: Javac.exe、java.exe、jar:exe等。这三者的关系:一层层的嵌套关系。JDK>JRE>JVM。

JVM的内存模型以及分区情况和作用

如下图所示

方法区

用于存储虛拟机加载的类信息,常量,静态变量等数据。

存放对象实例,所有的对象和数组都要在堆上分配。是JVM所管理的内存中最大的一块区域。

Java方法执行的内存模型:存储局部变量表,操作数栈,动态链接,方法岀口等信息。生命周期与线程相同。

本地方法栈

作用与虚拟机栈类似,不同点本地方法栈为η ative方法执行服务,虛拟机栈为虚拟机执

行的Java方法服务。

程序计数器

当前线程所执行的行号指示器。是JVM内存区域最小的一块区域。执行字节码工作时就

是利用程序计数器来选取下一条需要执行的字节码指令。

直接内存
    直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存也被频繁使用,而且也可能导致OutOfMemoryError异常出现。
    在JDK 1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方法,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

import sun.nio.ch.DirectBuffer;
 
import java.nio.ByteBuffer;
 
public class Main {
 
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Hello World!");
        ByteBuffer bb = ByteBuffer.allocateDirect(1024 * 1024 * 128);
        Thread.sleep(10000);
        ((DirectBuffer)bb).cleaner().clean();
        Thread.sleep(10000);
    }
}

堆外内存,其实就是不受JVM控制的内存。相比于堆内内存有几个优势: 
        1 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到) 
        2 加快了复制的速度。因为堆内在flush到远程时,会先复制到直接内存(非堆内存),然后通过Socket发送;而堆外内存相当于省略掉了这个工作。 
劣势: 
        1 堆外内存难以控制,如果内存泄漏,那么很难排查 
        2 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。

堆里面的分区:Eden,survival (from+ to),老年代,各自的特点。

 

堆里面分为新生代和老生代(java8 取消了永久代,采用了 Metaspace),新生代包 含 Eden+Survivor 区,survivor 区里面分为 from 和 to 区,内存回收时,如果用的是复 制算法,从 from 复制到 to,当经过一次或者多次 GC 之后,存活下来的对象会被移动 到老年区,当 JVM 内存不够用的时候,会触发 Full GC,清理 JVM 老年区当新生区满了之后会触发 YGC,先把存活的对象放到其中一个 Survice 区,然后进行垃圾清理。因为如果仅仅清理需要删除的对象,这样会导致内存碎 碎 片,因此一般会把 Eden 进行完全的清理,然后整理内存。那么下次 GC 的时候, 就会使用下一个 Survive,这样循环使用。如果有特别大的对象,新生代放不下, 就会使用老年代的担保,直接放到老年代里面。因为 JVM 认为,一般大对象的存 活时间一般比较久远。

JVM对象创建步骤流程是什么

整体流程如下图所示

 

第1步:虚拟机遇到一个new指令,首先将去检査这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用的类是否已经被加载&解析&初始化。

第2步:如果类已经被加载那么进行第3步;如果没有进行加载,那么就就需要先进行类的加载。

第3步:类加载检査通过之后,接下来进行新生对象的内存分配。

第4步:对象生成需要的内存大小在类加载完成后便可完全确定,为对象分配空间等同于把一块确定大小的内存从Java堆中划分出来

第5步:内存大小的划分分为两种情况

  • 第一种情况:JVM的内存是规整的,所有的使用的内存都放到-边,空闲的内存在另外一边,中间放一个指针作为分界点的指示器。那么这时候分配内存就比较简单,只要讲指针向空闲空间那边挪动一段与对象大小相同的距离。这种就是“指针碰撞”。
  • 第二种情况:JVM的內存不是规整的,也就是说已使用的內存与未使用的内存相互交错。这时候就没办法利用指正碰撞了。这时候我们就需要维护一张表,用于记录那些内存可用,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新到记录表上。

第6步:空间申请完成之后,JVM需要将内存的空间都初始化为0值。如果使用TLA就可以在TLAB分配的时候就可以迸行该工作。

第7步:JVM对对象进行必要的设置。例如,这个对象是哪个类的实例、对象的哈希码、GC年代等信息

第8步:完成了上面的步骤之后从JVM来看一个对象基本上完成了,但从Java程序代码绝对来看,对象创建才刚刚开始,需要执行κinit>方法,按照程序中设定的初始化操作初始化,这时候一个真正的程序对象生成了。

垃圾回收算法有几种类型?他们对应的优缺点又是什么?

常见的垃圾回收算法有:

标记清除算法、复制算法、标记整理算法、分代收集算法

标记清除算法

标记清除算法包括两个阶段:“标记”和“清除

标记阶段:确定所有要回收的对象,并做标记。

清除阶段:将标记阶段确定不可用的对象清除。

缺点

1.标记和清除的效率都不高。

2.会产生大量的碎片,而导致频繁的回收。

复制算法

内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,

把存活的对象复制到另一块上,然后把这块内存整个清理掉。

缺点:

1.需要浪费额外的內存作为复制区

2.当存活率较高时,复制算法效率会下降。

标记整理算法

标记_整理算法不是把存活对象复制到另一块內存,而是把存活对象往內存的一端移

动,然后直接回收边界以外的内存

缺点:

算法复杂度大,执行步骤较多

分代收集算法

目前大部分JVM的垃圾收集器采用的算法。根据对象存活的生命周期将内存划分为若干

个不同的区域。一般情况下将堆区划分为新生代( Young Generation和老年代( Tenured

Generation),永久代( Permanet generation)老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

如下图所示

 

Young:存放新创建的对象,对象生命周期非常短,几乎用完可以立即回收,也叫Eden

Tenured: young区多次回收后存活下来的对象将被移到 tenured区,也叫old区。

perm:永久带,主要存加载的类信息,生命周期长,几乎不会被回收缺点算法复杂度大,执行步骤较多

简单介绍一下什么是类加载机制?

Class文件由类装载器装载后,在JVM中将形成-份描述Cass结构的元信息对象,通过该元信息对象可以获知clas的结构信息:如构造函数,属性和方法等。虚拟机把描述类的数据从clas文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机

类的加载过程是什么?简单描述一下每个步骤

类加载的过程包括了

加载、验证、准备、解析、初始化五个阶段

第一步:加载

查找并加载类的二进制数据

加载是类加载过程的第一个阶段,虚拟机在这一阶段需要完成以下三件事情

  1. 通过类的全限定名来获取其定义的二进制字节流
  2. 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在Java堆中生成一个代表这个类的 java. lang Class对象,作为对方法区中这些数据的访问入口

第二步:验证

确保被加载的类的正确性

这一阶段是确保 Class文件的字节流中包含的信息符合当前虚拟机的规范,井且不会损害虚拟机自身的安全。包含了四个验证动作:文件格式验证,元数据验证,字节码验证,符号引用验。

第三步:准备

为类的静态变量分配内存,并将其初始化为默认值。

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。

第四步:解析

把类中的符号引用转换为直接引用

将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用类变量进行初始化为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始

JVM预定义的类加载器有哪几种?分别什么作用

启动( Bootstrap)类加载器

引导类装入器是用本地代码实现的类装入器,它负责将<Java_ Runtime home>/ib下面

的类库加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接

获取到启动类加载器的引用

标准扩展( Extension)类加载器

扩展类加载器负责将< Java runtime home>/ib/ext或者由系统变量 java ext. dir指定位

置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。

应用程序类加载器( Application)

应用程序类加载器( Application ClassLoader):负责加载用户路径( classpath)上的类

什么是双亲委派模式?有什么作用?

基本定义:

双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己

去加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请

求最终都应该被传递到顶层的启动类加载器中,只有当父加载器没有找到所需的类时

子加载器才会尝试去加载该类。

双亲委派机制:

1.当 AppClassLoader加载一个cass时,它首先不会自己去尝试加载这个类,而是把

类加载请求委派给父类加载器 Extclassloader去完成。

2当 Extclassloader加载一个cass时,它首先也不会自己去尝试加载这个类,而是

把类加载请求委派给 BootStrap ClassLoader去完成

3.如果 BootstrapClassLoader加载失败,会使用 Extclassloader来尝试加载;

4.若 Extclassloader也加载失败,则会使用 AppClassLoader来加载,如果

AppClassLoader也加载失败,则会报出异常 ClassNotFound Exception

如下图所示

 

双亲委派作用

  • 通过带有优先级的层级关可以避免类的重复加载
  • 保证Java程序安全稳定运行,Java核心AP定义类型不会被随意替换。

SafePoint 是什么

比如 GC 的时候必须要等到 Java 线程都进入到 safepoint 的时候 VMThread 才能开始

执行 GC

1. 循环的末尾 (防止大循环的时候一直不进入 safepoint,而其他线程在等待它进入

safepoint)

2. 方法返回前

3. 调用方法的 call 之后

4. 抛出异常的位置

介绍一下JVM中垃圾收集器有哪些?他们特点分别是什么?

 

新生代垃圾收集器

Seria收集器

特点

Serial收集器只能使用一条线程进行垃圾收集工作,并且在进行垃圾收集的时候,所有

的工作线程都需要停止工作,等待垃圾收集线程完成以后,其他线程才可以继续工作。

使用算法: 复制算法

ParDew收集器

特点

ParDew垃圾收集器是 Serial收集器的多线程版本。为了利用CPU多核多线程的优势,

ParDew收集器可以运行多个收集线程来进行垃圾收集工作。这样可以提高垃圾收集过程

的效率。

使用算法: 复制算法

Parallel Scavenge收集器

特点

Parallel Scavenge收集器是一款多线程的垃圾收集器,但是它又和 ParDew有很大的不同

Parallel Scavenge收集器和其他收集器的关注点不同。其他收集器,比如 ParDew和CMS

这些收集器,它们主要关注的是如何缩短垃圾收集的时间。而 Parallel Scavenge收集器

关注的是如何控制系统运行的吞吐量。这里说的吞吐量,指的是CP∪用于运行应用程序

的时间和CPU总时间的占比,吞吐量=代码运行时间/(代码运行时间+垃圾收集时

间)。如果虚拟机运行的总的CPU时间是100分钟,而用于执行垃圾收集的时间为1分

钟,那么吞吐量就是99%。

使用算法: 复制算法

老年代垃圾收集器

Serial old收集器

特点

Serial old收集器是 Serial收集器的老年代版本。这款收集器主要用于客户端应用程序中

作为老年代的垃圾收集器,也可以作为服务端应用程序的垃圾收集器。

使用算法: 标记整理

Parallel old收集器

特点

Parallel old收集器是 Parallel Scavenge收集器的老年代版本这个收集器是在JDKL.6版本

中出现的,所以在JDK1.6之前,新生代的 Parallel Scavenge只能和 Serial old这款单线程

的老年代收集器配合使用。 Parallel old垃圾收集器和 Parallel Scavenge收集器一样,也

是一款关注吞吐量的垃圾收集器,和 Parallel Scavenge收集器一起配合,可以实现对

Java堆内存的吞吐量优先的垃圾收集策略。

使用算法: 标记整理

CMS收集器

特点

CMS收集器是目前老年代收集器中比较优秀的垃圾收集器。CMS是 Concurrent mark

swep,从名字可以看出,这是一款使用标记清除”算法的并发收集器。

CMS垃圾收集器是一款以获取最短停顿时间为目标的收集器。如下图所示

 

从图中可以看出,CMs收集器的工作过程可以分为4个阶段

初始标记( CMS initial mark)阶段

并发标记( CMS concurrent mark)阶段

重新标记( CMS remark)阶段

并发清除( (CMS concurrent sweep)阶段

使用算法:复制标记清除

其他

G1垃圾收集器

特点

主要步骤:初始标记,并发标记,重新标记,复制清除

使用算法:复制+标记整理

G1与CMS两个垃圾收集器的对比

细节方面不同

1.G1在压缩空间方面有优势。

2.G1通过将内存空间分成区域( Region)的方式避免内存碎片问题。

3.Eden, Surviⅳor,old区不再固定、在内存使用效率上来说更灵活。

4.G1可以通过设置预期停顿时间( Pause Time)来控制垃圾收集时间避免应用雪崩现象

5.G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做

6.G1会在 Young GC中使用、而CMS只能在O区使用。

整体内容不同

吞吐量优先:G1

响应优先:CMS

CMs的缺点是对cpu的要求比较高。G1是将内存化成了多块,所有对内段的大小有很大

的要求。

CMS是清除,所以会存在很多的内存碎片。G1是整理,所以碎片空间较小。

线上常用的JVM参数有哪些?

数据区设置

Xms:初始堆大小

Xmx:最大堆大小

Xs:Java每个线程的 Stack大小

XX: New Size=n:设置年轻代大小

XX: NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比

值为1:3,年轻代占整个年轻代年老代和的1/4。

XX: SurvivorRatio=n:年轻代中Eden区与两个 Survivor区的比值。注意 Survivor

区有两个。如:3,表示Eden: Survivor=3:2,一个 Survivor区占整个年轻代的

XX: MaxPerm size=n:设置持久代大小。

收集器设置

XX:+ UseserialgC:设置串行收集器

X+ UseParallelGC:设置并行收集器

X+ UseParalledloldgc:设置并行年老代收集器

XX:+ UseConcMarkSweepGC:设置并发收集器

GC日志打印设置

XX+ PrintGC:打印GC的简要信息

·XX:+ PrintgCDetails:打印GC详细信息

XX:+ PrintGCTimeStamps:输出GC的时间戳

Minor GC 与 Full GC 分别在什么时候发生?

新生代内存不够用时候发生 MGC 也叫 YGC,JVM 内存不够的时候发生 FGC

什么是Cass文件?Cass文件主要的信息结构有哪些?

Class文件是一组以8位字节为基础单位的二进制流。各个数据项严格按顺序排列

Class文件格式采用一种类似于C语言结构体的伪结构来存储数据。这样的伪结构仅仅有两种数据类型:无符号数和表。

无符号数:是基本数据类型。以u1、u2、u4、U8分别代表1个字节、2个字节、4个字节、8个字节的无符号数,能够用来描写叙述数字、索引引用、数量值或者依照UTF-8编码构成的字符串值。

:由多个无符号数或者其它表作为数据项构成的复合数据类型。全部表都习惯性地以

info结尾

对象对象已“死”是什么概念

对象不可能再被任何途径使用,称为对象已死。

判断对象已死的方法有:引用计数法与可达性分析算法

Java语言怎么实现跨平台的?

我们编写的Java源码,编译后会生成一种. class文件,称为字节码文件。字节码不能直

接运行,必须通过JVM翻译成机器码才能运行。JVM是一个”桥梁“,是一个”中间件“,是实现跨平台的关键。Java代码首先被编译成字节码文件,再由JVM将字节码文件翻译成机器语言,从而达到运行Java程序的目的

JVM数据运行区,哪些会造成OOM的情况

Java 内存溢出(java.lang.OutOfMemoryError)常见情况和总结_vincent-CSDN博客_java.lang.outofmemoryerror

除了数据运行区,其他区域均有可能造成OOM的情况。

堆溢出:java.Lang. OutofMemoryError: Java heap space

栈溢出:java.Lang. StackOverflowerror

永久代溢出:java.Lang. OutofMemoryError: PermGen space

详细介绍一下对象在分带内存区域的分配过程

1.JVM会试图为相关Java对象在Eden中初始化一块内存区域

2.当Eden空间足够时,内存申请结束;否则到下一步

3.JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收)。释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区

4. Survivor区被用来作为Eden及Old的中间交换区域,当old区空间足够时Survivor区的对象会被移到old区,否则会被保留在Survivor区。

5.当Old区空间不够时,JVM会在Old区进行完全的垃圾收集。

6.完全垃圾收集后,若 Survivor及Old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现“ out of memory”错误

对象什么时候进入老年代?

对象优先在Eden区分配内存

当对象首次创建时,会放在新生代的eden区,若没有GC的介入,会一直在eden区,GC

后,是可能进入 survivor区或者年老代

大对象直接进入老年代

JVM参数-XX:PretenureSizeThreshold的意思就是将体积大于这个设置值的对象直接在老年代分配。 这样做是为了避免在Eden区及两个Survivor区之间发生大量的内存复制。
 PretenureSizeThreshold参数只对 Serial 和 ParNew两款收集器有效。

长期存活的对象进入老年代

虚拟机给每个对象定义了一个对象年龄(Age)计数器,对象在 Survivor区中每熬过一次

Minor gc,年龄就增加1,当他的年龄增加到一定程度(默认是15岁),就将会被晋升

到老年代中

 
 

简述 java 内存分配与回收策率以及 Minor GC 和 Major GC

1. 对象优先在堆的 Eden 区分配。

2. 大对象直接进入老年代.3. 长期存活的对象将直接进入老年代.

当 Eden 区没有足够的空间进行分配时,虚拟机会执行一次 Minor GC.Minor Gc 通

常发生在新生代的 Eden 区,在这个区的对象生存期短,往往发生 Gc 的频率较高,

回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代 GC

的时候不会触发 Minor GC,但是通过配置,可以在 Full GC 之前进行一次 Minor

GC 这样可以加快老年代的回收速度

System.gc() 和 Runtime.gc() 会做什么事情?

这两个方法用来提示 JVM 要进行垃圾回收。但是,立即开始还是 延迟进行垃圾回收是取决于 JVM 的。

什么是分布式垃圾回收(DGC)?它是如何工作的?

DGC 叫做分布式垃圾回收。RMI 使用 DGC 来做自动垃圾回收。 因为 RMI 包含了跨虚拟机的远程对象的引用,垃圾回收是很困难 的。DGC 使用引用计数算法来给远程对象提供自动内存管理。

串行(serial)收集器和吞吐量(throughput)收集器的区别 是什么?

吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模 和大规模数据的应用程序。 而串行收集器对大多数的小应用(在现代处理器上需要大概 100M 左右的内存)就足够了。

什么是内存溢出,内存泄露?他们的区别是什么?

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出

现 out of memory;

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内

存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

区别:内存溢出,是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢

出;内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还

( delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丟

了),而系统也不能再次将它分配给需要的程序。

栈上分配与逃逸分析(Escape Analysis)

什么是栈上分配?

   栈上分配主要是指在Java程序的执行过程中,在方法体中声明的变量以及创建的对象,将直接从该线程所使用的栈中分配空间。 一般而言,创建对象都是从堆中来分配的,这里是指在栈上来分配空间给新创建的对象。

什么是逃逸?

   逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到;这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量引用。正常的方法调用中,方法体中创建的对象将在执行完毕之后,将回收其中创建的对象;故由于无法回收,即成为逃逸。


public class StackAllocation {


    public StackAllocation obj;


    /**
     *  方法返回StackAllocation对象,发生逃逸
     * @return
     */

    public StackAllocation getInstance(){

        return  obj == null ? new StackAllocation() : obj;

    }


    /**
     * 为成员变量赋值,发生逃逸
     */

    public void setObj(){

        this.obj = new StackAllocation();

    }



    /**
     * 对象作用域在方法体内,没有发生逃逸
     */

    public void useStackAllocation(){

        StackAllocation stackAllocation = new StackAllocation();

    }


    /**
     * 引用成员变量,发生逃逸
     */

    public void useStackAllocationObj(){

        StackAllocation stackAllocation = getInstance();

    }

}

 逃逸分析   

    在JDK 6之后支持对象的栈上分析和逃逸分析,在JDK 7中完全支持栈上分配对象。 其是否打开逃逸分析依赖于以下JVM的设置: -XX:+DoEscapeAnalysis  

栈上分配与逃逸分析的关系  

    进行逃逸分析之后,产生的后果是所有的对象都将由栈上分配,而非从JVM内存模型中的堆来分配 。

逃逸分析/栈上分配的优劣分析

优势表现在以下两个方面:

   消除同步。线程同步的代价是相当高的,同步的后果是降低并发性和性能。逃逸分析可以判断出某个对象是否始终只被一个线程访问,如果只被一个线程访问,那么对该对象的同步操作就可以转化成没有同步保护的操作,这样就能大大提高并发程度和性能。

 矢量替代。逃逸分析方法如果发现对象的内存存储结构不需要连续进行的话,就可以将对象的部分甚至全部都保存在CPU寄存器内,这样能大大提高访问速度。

  劣势:  

栈上分配受限于栈的空间大小,一般自我迭代类的需求以及大的对象空间需求操作,将导致栈的内存溢出;故只适用于一定范围之内的内存范围请求。

引起类加载操作的行为有哪些?

1.遇到new、 getstatic、 putstatic或 invokestatic这四条字节码指令。

2.反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

3.子类初始化的时候,如果其父类还没初始化,则需先触发其父类的初始化

4虚拟机执行主类的时候(有main( stringl args))

5.JDK1.7动态语言支持

介绍一下JVM提供的常用工具

1.jps:用来显示本地的Java进程,可以查看本地运行着几个Java程序,并显示他们的进程号。

命令格式:jps

2. jinfo:运行环境参数: Java System属性和JVM命令行参数, Java class path等信

命令格式:jinfo 进程pid

3. jstat:监视虛拟机各种运行状态信息的命令行工具

命令格式: jstat -gc 123 250 20

4. jstack:可以观察到JVM中当前所有线程的运行情况和线程当前状态

命令格式: jstack 进程pid

5. jmap:观察运行中的JVM物理内存的占用情况(如:产生哪些对象,及其数

命令格式: jmap [option ] pid

Full GC、 Major GC、 Minor gc之间区别?

Minor GC

从新生代空间(包括Eden和 Survivor区域)回收内存被称为 Minor gc

Major GC

清理 Tenured区,用于回收老年代,出现 Major GC通常会出现至少一次 Minor g

Full GC

FuGC是针对整个新生代、老年代、元空间( metaspace,java8以上版本取代perm gen)的全局范围的GC。

什么时候触发Full GC?

1.调用 System.gc时,系统建议执行FuGC,但是不必然执行。

2.老年代空间不足

3.方法区空间不足

4.通过 Minor gc后进入老年代的平均大小大于老年代的可用内存。

5.由Eden区、 survivor space1( From Space)区向 survivor space2( To Space)区复制时,对象大小大于 To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

什么情况下会出现栈溢出

1方法创建了一个很大的对象,如Lst,Aray

2.是否产生了循环调用、死循环

3.是否引用了较大的全局变量

说一下强引用、软引用、弱引用、虚引用以及他们之间和gc的关系

1强引用:neW出的对象之类的引用,只要强引用还在,永远不会回收。

2.软引用:引用但非必须的对象,内存溢出异常之前,回收。

3.弱引用:非必须的对象,对象能生存到下一次垃圾收集发生之前。

4.虚引用:对生存时间无影响,在垃圾回收时得到通知。

Eden和 Survivor的比例分配是什么情况?为什么

默认比例8:1。

大部分对象都是朝生夕死

复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就

将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片,

实战题

CPU资源占用过高

1.top查看当前CPU情况,找到占用CPU过高的进程PID=123

2.top -H -p123找出两个CpU占用较高的线程,记录下来PD=2345,3456转换为十六进制

3. jstack -l 123 > temp. txt打印出当前进程的线程栈

4.查找到对应于第二步的两个线程运行栈,分析代码。

OOM异常排查

1.使用top指令查询服务器系统状态

2.ps- aux|grep java找出当前Java进程的PID

3. jstat- gcutil pid interval查看当前GC的状态

4.jmap- histo:live pid可用统计存活对象的分布情况,从高到低查看占据内存最多的

对象

5.jmap- dump: format=b,fle=文件名【pid】利用 Jmap dump

使用性能分析工具对上一步dump出来的文件进行分析,工具有MAT等。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐