JVM

内存溢出是怎么回事?请举例。**

答:

== 和equals 的区别?**

答:

(short,boolean,int,byte,char,long,double,float) ==和equals都表示:比对变量的值是否相等。

== 表示比对的是两个对象的地址是否相等; equals 已经不再是比对内存地址,例如:String比对两个字符串对象的字面量是否相等;

== 表示比对的是对象的地址是否相等; equals Object中equals方法的初始行为是比对两个对象的内存地址。所有对象都继承于Object这个基类,因此,子类可能会覆盖父类的equals方法,对于覆盖equeals方法的类,会由实现方法决定比对行为。

hashCode方法的作用,为什么重写equals方法,建议一起重写hashcode方法?**

答:

NIO是什么?适用于何种场景

简述JVM内存结构

答:

JVM在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。 * 程序计数器 * 线程私有,是当前线程所执行的字节码的行号指示器 * 通过改变这个计数器的值来选择下一条需要执行的字节码指令:分支,循环,跳转,异常处理,线程恢复等 * 唯一一个没有规定任何OutofMemoryError情况的区域。

JVM堆的基本结构

JVM的内存分配

如何用Java分配一段连续的1G的内存空间?需要注意些什么

如何查看JVM的内存使用情况

为什么需要GC,都有哪些回收算法

答:

GC是垃圾回收的意思,在JVM中,几乎所有的对象和变量信息都保存在内存的堆和栈中。在java堆中,创建对象时,为对象分配内存,在对象生命周期结束时,需要对对象所占用的的内存进行回收,以供为后续新生成对象分配空间复用。如果不能够对这些内存进行回收,则会用完所有内存,导致系统内存溢出,甚至系统崩溃。因此,需要有GC回收内存。 GC算法思想有如下几种: * 标记-整理算法 * 标记-清除算法 * 分代收集算法

CMS垃圾回收的基本流程

Jvm有自己的内存回收机制,但为什么还存在内存泄露的问题呢

答:

java内存泄露的根本原因:内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式。也称作:无意识的对象保持。 java的GC机制: * 引用计数算法:计数器 * 可达性分析 通过一系列的称为“GC roots”的对象最为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC roots没有任何引用链相连时,则证明此对象是不可引用的。

GC roots:

java中对内存对象的访问,使用的是引用的方式。在 Java 代码中我们维护一个内存对象的引用变量,通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间。在 Java 程序中,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)。 GC 线程会从代码栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的。如果 GC 线程通过这种方式,无法跟踪到某一块堆内存,那么 GC 就认为这块内存将不再使用了(因为代码中已经无法访问这块内存了)。

通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError

Java程序是否会内存溢出,内存泄露情况发生?举几个例子

常用的GC策略,什么时候会触发YGC,什么时候触发FGC

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

finalize()方法什么时候被调用?析构函数(finalization)的目的是什么

如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用它的内存

创建一个类的实例都有哪些办法?(OK)

答:

* new 创建对象

  Person person = new Person();

* 反射机制1

  Person emp2 = (Person) Class.forName("org.programming.mitra.exercises.Person").newInstance();
    或者
  Persone emp2 = Person.class.newInstance();

* 反射机制2

  Constructor<Person > constructor = Person .class.getConstructor();
  Person emp3 = constructor.newInstance();

* clone

  class Person implements Cloneable{
  ...}
  Person p1 = new Person();
  Person p2 = null;
  try{
    p2 = (Person)p1.clone();
  }catch(CloneNotSupportedException e){
  e.printStackTrace();}

* 反序列化

  // By using Deserialization
  // Serialization
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"));
  out.writeObject(emp4);
  out.close();
  //Deserialization
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
  Employee emp5 = (Employee) in.readObject();
  in.close();
  emp5.setName("Akash");
  System.out.println(emp5 + ", hashcode : " + emp5.hashCode());

判断对象是否存活的方法?

Java 类加载过程?

 根据类的全限定名,获取二进制字节流
 将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构
 在内存中生产一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

 目的:确保class文件字节流中包含的信息符合当前虚拟机的要求,不会危害到虚拟机自身的安全

 1. 验证class文件格式,字节流是否符合class文件格式要求,是否能被虚拟机处理;例如:是否以魔数开头(0xCAFEBABE)开头,主次版本号是否在当前虚拟机支持范围内,常量池中是否有不被支持的常量;
 2. 元数据验证:保证描述的信息符合java语言规范;例如:是否有父类,是否继承了不允许被继承的类,是否实现了接口中的方法,是否与父类产生矛盾;
 3. 字节码验证:通过数据流和控制流分析,确定程序语义是合法的,符合语义的;例如:保证跳转指令不会跳转到方法体以外的字节码上;保证方法体上的类型转换是有效的;
 4. 符号引用验证,确保解析动作能正常执行:对类自身以外的信息进行匹配性校验(常量池中的各种符号引用);例如:符号引用中通过字符串描述的全限定名是否能找到对应的类;

 备注:验证非虚拟机必须的阶段,可以通过-Xverify:none 跳过大部分验证
  为变量分配内存,并为类变量设置初始值的阶段
  在方法区中进行分配
  进行内存分配的只是类变量(被static修饰的变量),不包括实例变量
 虚拟机将常量池内的符号引用替换为直接引用的过程
 1. 类或者接口的解析
 2. 字段解析
 3. 类方法解析
 4. 接口方法解析
 真正开始执行类中定义的Java程序代码
 初始化阶段是执行类构造器<clinit>()方法的过程

描述一下JVM加载Class文件的原理机制

什么是类加载器,类加载器有哪些方法

用于实现类的加载动作
确定类在java虚拟机中的唯一性
每个类加载器都有一个独立的类名称空间
比较两个类是否相同:只有在两个类是由同一个类加载器加载的前提下才有意义

 1. 启动类加载器(Bootstrap ClassLoader),用C++实现,是虚拟机的一部分;
 2. 所有其他的类加载器,由java语言实现,独立于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader
 1. 启动类加载器;<JAVA_HOME>/lib目录中,或者-Xbootclasspath参数所指定的路径中的类,并且是虚拟机识别的类(按照名字);
 2. 扩展类加载器:由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,可以直接使用扩展类加载器;
 3. 应用程序类加载器:由sun.misc.Launcher$AppClassLoader实现,加载用户类路径上的类

类加载器双亲委派模型机制

:
           启动类加载器
                ^
                |
           扩展类加载器
                ^
                |
         应用程序类加载器
           ^         ^
           |         |

自定义类加载器    自定义类加载器

*优点:

 通用的类(例如java.lang.Object),可以由最顶端的启动类加载器进行加载,保证类体系中最基础的行为。
 如果自己编写Object的加载方法,加载方法不一样,将会变得一片混乱。

*双亲委派模型对于Java程序的稳定运行有很重要的作用。 * 代码实现全部在java.lang.ClassLoader的loadClass()中

JVM有哪些常用启动参数可以调整,描述几个?

  设置JVM最大可用内存为3550M
  设置JVM初始内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存
  设置每个线程的堆栈大小。(JDK5.0以后每个线程堆栈大小为1M)
  设置年轻代大小为1024m。等效于同时配置NewSize和MaxNewSize两个。
  设置年轻代初始值为1024M。
  设置年轻代最大值为1024M。
-XX:+UseSerialGC:设置串行收集器。
  并行收集器(吞吐量优先)
-XX:+UseParallelGC:设置年轻代为并行收集器。(此时年老代仍然为串行)
-XX:+UseParallelOldGC:配置年老代为并行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数。
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间(单位毫秒)。如果无法满足此时间,JVM会自动调整年轻代大小,以满足此时间。
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动调整年轻代Eden区大小和Survivor区大小的比例,以达成目标系统规定的最低响应时间或者收集频率等指标。此参数建议在使用并行收集器时,一直打开。
  
-XX:+UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集。
-XX:+UseParNewGC:设置年轻代为并发收集。JDK5.0以上JVM会自行设置,无需再设。
-XX:CMSFullGCsBeforeCompaction=0:每次Full GC后立刻开始压缩和整理内存。
-XX:+UseCMSCompactAtFullCollection:打开内存空间的压缩和整理,在Full GC后执行。
-XX:+CMSIncrementalMode:设置为增量收集模式。一般适用于单CPU情况。
-XX:CMSInitiatingOccupancyFraction=70:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。

* 其它垃圾回收参数

-XX:+ScavengeBeforeFullGC:年轻代GC优于Full GC执行。
-XX:-DisableExplicitGC:不响应 System.gc() 代码。
-XX:+UseThreadPriorities:启用本地线程优先级API。即使 java.lang.Thread.setPriority() 生效,不启用则无效。
-XX:SoftRefLRUPolicyMSPerMB=0:软引用对象在最后一次被访问后能存活0毫秒(JVM默认为1000毫秒)。
-XX:TargetSurvivorRatio=90:允许90%的Survivor区被占用(JVM默认为50%)。提高对于Survivor区的使用率。
-XX:-CITime:打印消耗在JIT编译的时间。
-XX:ErrorFile=./hs_err_pid.log:保存错误日志或数据到指定文件中。
-XX:HeapDumpPath=./java_pid.hprof:指定Dump堆内存时的路径。
-XX:-HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时Dump出此时的堆内存。
-XX:OnError=";":出现致命ERROR后运行自定义命令。
-XX:OnOutOfMemoryError=";":当首次遭遇内存溢出时执行自定义命令。
-XX:-PrintClassHistogram:按下 Ctrl+Break 后打印堆内存中类实例的柱状信息,同JDK的 jmap -histo 命令。
-XX:-PrintConcurrentLocks:按下 Ctrl+Break 后打印线程栈中并发锁的相关信息,同JDK的 jstack -l 命令。
-XX:-PrintCompilation:当一个方法被编译时打印相关信息。
-XX:-PrintGC:每次GC时打印相关信息。
-XX:-PrintGCDetails:每次GC时打印详细信息。
-XX:-PrintGCTimeStamps:打印每次GC的时间戳。
-XX:-TraceClassLoading:跟踪类的加载信息。
-XX:-TraceClassLoadingPreorder:跟踪被引用到的所有类的加载信息。
-XX:-TraceClassResolution:跟踪常量池。
-XX:-TraceClassUnloading:跟踪类的卸载信息。

你常用的JVM配置和调优参数都有哪些?分别什么作用?

  -server -Xmx3550m -Xms3550m -Xmn1256m -Xss128k -XX:SurvivorRatio=6 -XX:MaxPermSize=256m -XX:ParallelGCThreads=8 -XX:MaxTenuringThreshold=0 -XX:+UseConcMarkSweepGC

  -Xmx 与 -Xms 相同以避免JVM反复重新申请内存。-Xmx 的大小约等于系统内存大小的一半,即充分利用系统资源,又给予系统安全运行的空间。
  -Xmn1256m 设置年轻代大小为1256MB。此值对系统性能影响较大,Sun官方推荐配置年轻代大小为整个堆的3/8。
  -Xss128k 设置较小的线程栈以支持创建更多的线程,支持海量访问,并提升系统性能。
  -XX:SurvivorRatio=6 设置年轻代中Eden区与Survivor区的比值。系统默认是8,根据经验设置为6,则2个Survivor区与1个Eden区的比值为2:6,一个Survivor区占整个  年轻代的1/8。
  -XX:ParallelGCThreads=8 配置并行收集器的线程数,即同时8个线程一起进行垃圾回收。此值一般配置为与CPU数目相等。
  -XX:MaxTenuringThreshold=0 设置垃圾最大年龄(在年轻代的存活次数)。如果设置为0的话,则年轻代对象不经过Survivor区直接进入年老代。对于年老代比较多的应  用,可以提高效率;如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率  。根据被海量访问的动态Web应用之特点,其内存要么被缓存起来以减少直接访问DB,要么被快速回收以支持高并发海量请求,因此其内存对象在年轻代存活多次意义不  大,可以直接进入年老代,根据实际应用效果,在这里设置此值为0。
  -XX:+UseConcMarkSweepGC 设置年老代为并发收集。CMS(ConcMarkSweepGC)收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并  发的垃圾回收线程来标记清除年老代内存,适用于应用中存在比较多的长生命周期对象的情况。
  -server -XX:PermSize=196m -XX:MaxPermSize=196m -Xmn320m -Xms768m -Xmx1024m
  调优说明:
  -XX:PermSize=196m -XX:MaxPermSize=196m 根据集成构建的特点,大规模的系统编译可能需要加载大量的Java类到内存中,所以预先分配好大量的持久代内存是高效和  必要的。
  -Xmn320m 遵循年轻代大小为整个堆的3/8原则。
  -Xms768m -Xmx1024m 根据系统大致能够承受的堆内存大小设置即可。

深拷贝和浅拷贝

如何创建线程

答:

在java中通过两种方式创建线程:

两种方法的不同:

线程和进程的差别是什么?

什么是线程安全,如何保证线程安全

答:

引起线程安全问题的根本原因是: * 多线程环境,即同时存在多个操作者,单线程环境不存在线程安全问题;

综上可知,线程安全问题产生的根本原因:

共享数据存在被并发修改的可能

线程安全已发的问题:

保证线程安全的方法:

只有存在共享数据时才需要考虑线程安全问题,从JVM管理的内存空间来看,在如下区域需要考虑线程安全

如何避免死锁

Volatile关键字的作用?

答:

并发编程中三个问题:

一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其它线程能够立即看得到修改的值。

即程序执行的顺序按照代码的先后顺序执行 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行 volatile也无法保证对变量的任何操作都是原子性的 * 观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令,lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: * 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;    * 它会强制将对缓存的修改操作立即写入主存;    * 如果是写操作,它会导致其他CPU中对应的缓存行无效;

使用volatile关键字的场景:

备注:

java通过集中原子操作完成工作内存和主内存的交互

HashMap在多线程环境下使用需要注意什么?为什么?

Java程序中启动一个线程是用run还是start?

什么是守护线程?有什么用?

什么是死锁?如何避免

答:

由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行,如果线程都不主动释放所占有的资源,将产生死锁 当然死锁的产生是必须要满足一些特定条件的: * 互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 * 请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放 * 不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用 * 循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞

避免死锁的方法:

如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生

在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求

每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。

检测到死锁,释放所有锁,回退,并且等待一段随机的时间后重试。

如何实现一个线程安全的数据结构

答:

  1. 常量,不需要线程安全
  2. 方法的局部变量,不需要线程安全;
  3. 类的成员变量,如果是单例模式,需要;如果不是,则不需要;
  4. 静态变量,需要线程安全;

编写线程安全类的几个原则:

Java里面的Threadlocal是怎样实现的?

ConcurrentHashMap的实现原理是?

sleep和wait区别

notify和notifyAll区别

volatile关键字的作

ThreadLocal的作用与实现

两个线程如何串行执行

上下文切换是什么含义

可以运行时kill掉一个线程吗?

synchronized关键字作用以及怎么使用?

synchronized 关键字的实现原理?

答:

Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

程序计数器会不会发生内存溢出?为什么?

答:不会

什么是死锁,如何避免死锁

什么是条件锁、读写锁、自旋锁、可重入锁?

线程池ThreadPoolExecutor的实现原理?

Thread dump文件如何分析(Runnable,锁,代码栈,操作系统线程ID关联)

Java服务端问题排查(OOM,CPU高,Load高,类冲突)

Java常用问题排查工具及用法(top, iostat, vmstat, sar, tcpdump, jvisualvm, jmap, jconsole)

如何查看Java应用的线程信息