JVM总结

jvm有哪些分区

类加载子系统、方法区、java堆、java虚拟机栈、本地方法栈、程序计数器、执行引擎

对象如何创建

常量池中类符号引用
指针碰撞
把Java堆中的内存一分为二,一边是所有用过的内存(这部分内存不能被分配了),一边是空闲的内存,是可以被分配的,这样的话,在可用于不可用的内存之间会有一个分割点指示器,那么为对象分配内存实际上就是从这个分界点指示器往空闲内存的一边拨动一段空间就可以了

空闲列表
已使用的内存与空闲内存可能是交叉在一起的,那么使用指针碰撞的方式分配内存就会产生问题,但是虚拟机维护着一张列表,这张列表记录了哪些区域的内存是可用的,那么在分配内存的时候就从选择可以容纳对象要求大小的内存区域分配给这个对象

对象头设置一些必要信息,从虚拟机的角度看,一个对象已经构造完成,但是从开发人员的角度看,还需要进行new对象之后初始化,接着执行init方法,到这里一个对象才算真正创建完毕

jvm的内存布局

对象头、实例数据和对齐填充
对象头又包括两部分信息,第一部分用于存储对象自身的运行时数据,比如哈希码、GC分代年龄,第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

实例数据部分是对象真真好存储的有效信息,也是程序代码中所定义的各种类型的字段内容

第三部分不是必然存在的,只是起到占位符的作用,因为HotspotVM规定对象的起始地址必须是8字节的整数倍。所以很有可能以上两部分的大小不够8字节的整数倍,那么这个字段就可以发挥作用了。

java如何进行对象访问

主要是通过Java栈中的reference数据,通过这个reference数据只是一个指向对象的引用,
目前主流的对象访问方式主要由句柄和直接指针两种
通过句柄访问的话,会在Java堆中划分出一块句柄池,句柄池中句柄存放了对象的实例数据和类型指针,而reference数据则存放了句柄的地址引用。使用直接指针访问对象,那么reference数据存放的就是对象的地址
使用句柄访问的最大好处是reference中存储的稳定的句柄地址,当对象的地址发生了改变可以不用去关心。而直接指针的最大好处是速度更快,在于节省了一次指针定位的时间

OOM是啥,分析及解决

内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。
内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出

java.lang.OutOfMemoryError: Java heap space ——>java堆内存溢出,堆大小可以通过虚拟机参数-Xms,-Xmx等修改

java.lang.OutOfMemoryError: PermGen space ——>java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

java.lang.StackOverflowError:一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小

jvm堆内存情况

堆内存中主要分为年轻代和老年代,主要分为Eden(对象都会在该区域内创建)同时也会在s0或s1中随机一个进行创建,经过一次GC后在s0和s1中使用复制算法保留存活下来的对象
流程:年轻代:通过标记-复制-清除(15次GC,老年代)
老年区:通过标记-清除-整理进行垃圾回收,如果老年代内存不足就会触发FULL GC.默认是占用了68%后收集(可通过-XX:CMSInitiatingOccupancyFraction=68设置),该GC非常影响性能

什么是可达性分析

是通过一系列的称为“GC Roots”的对象作为起始点,从这些起始点开始向下搜索,所走过的路径称为引用链,如果一个对象到GC Roots没有任何引用链,那么这个对象是不可用的,就是说,程序中没有谁引用了这个对象,所以可以说从根节点到叶子结点是不可达的
虚拟机栈(栈帧中本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(也就是native本地方法)引用的对象

为什么需要两次标记

原因是如果一个对象没有与GC Roots结点相连,就会被第一次标记,而如果对象覆盖了finalize方法,并且在finalize方法中与某个对象建立了引用关系,那么第二次标记会失败,那么这个对象就会被移出“即将回收”的对象列表,移出之后这个对象就“活”了下来,如果在finalize方法中这个对相关仍然没有与一个对象建立引用关系,那么这个对象就真正死亡了

方法区中的什么对象可以被回收

方法区中的永久代的垃圾回收主要为两部分: 废弃常量和无用的类
废弃常量是指没有任何对象引用常量池中的某个对象,那么这个对象就会被回收,而对于常量池中的垃圾回收只要进行一次标记就可以进行判断
无用的类需要满足以下三个条件才可以宣判一个类的“死刑”:
1.该类的所有实例都已经被回收,也就是Java堆中不存在该类的实例
2.加载该类的ClassLoader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

jvm堆参数简单说明

-XX 对于系统级别的jvm配置 配置日志信息 垃圾回收器类型
非 -XX的 基本都是对 应用层面上的配置
启用 - 禁用
-XX :PrintGC 使用这个参数,虚拟机启动后,只要遇到GC就会打印日志
-XX: +UserSerialGC 配置串行回收器
-XX:+PrintGCDetails:可以查看详细信息,包括各个区的情况
-Xms: 设置java程序启动时初始堆大小
-Xmx: 设置java程序能获得的最大堆大小
-Xloggc:路径 :将打印出来的日志信息保存至指定的路径
-Xmn:设置堆的内存大小
-XX:SurvivorRatio=m:n调整Eden和Survivor的比例为m:n

有哪些垃圾收集器

Serial收集器: 它是一种单线程垃圾收集器,这就意味着在其进行垃圾收集的时候需要暂停其他的线程

ParNew收集器: 为Serial收集器的多线程版本,能够与CMS收集器配合工作

Parallel Scavenge收集器:
其使用的算法是复制算法,也是并行的多线程收集器(所谓”并行”,就是指多条垃圾收集线程同时工作,但是用户仍处于等待状态)
更关注可控制的吞吐量,吞吐量等于运行用户代码的时间/(运行用户代码的时间+垃圾收集时间)。

Serial Old收集器:
Parallel Old收集器
CMS收集器:获取最短回收停顿时间。在注重服务器的响应速度,希望停顿时间最短
G1收集器: G1收集器将Java堆划分为多个大小相等的Region(独立区域),新生代与老年代都是一部分Region的集合,G1的收集范围则是这一个个Region

java类加载机制如何

1.并非一次性加载
2.需要的时候加载(运行期动态加载)
3.static语句块在加载后执行一次
4.dynamic语句块每次new新的对象都会执行(等同于构造方法中语句)

jdk中有各种各样的ClassLoad将类加载进内存
一:bootstrap class loader
它是最核心的类用于加载java中核心的类
二:extesion class loader
三:application class loader
也就是说它是java的核心类用于加载用户自定义的类和其他类
四:other class loaders
SecureClassLoader、URLClassLoader等等,该类是其他加载类加载其它类

热评文章