JVM 核心1----JVM的认识

JVM核心一

JVM的认识

可能遇到的问题

1.谈谈对jvm的理解? java8虚拟机和之前的变化更新?

1)jvm的作用:首先java语音最重要的特点就是跨平台运行,而这特点就是使用了JVM实现的。
2)JVM俗称Java虚拟机,虚拟机是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
3)jvm的内部结构,分成三部分,类装载器,运行时数据区,执行引擎。
4)JVM垃圾回收机制(GC)
java8虚拟机和之前的变化是:删除永久区变为元空间。

2.什么是OOM?

程序申请内存过大,虚拟机无法满足我们,然后自杀了。
类似于Android系统的APP每个进程或者虚拟机有最大内存限制,一旦超过这个限制系统就会抛出OOM错误。

3.什么是栈溢出Stack Overflow Error? 怎么分析?

## 出现栈内存溢出的常见原因有2个:
1> 函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈。
2> 局部静态变量体积太大
## 解决办法大致说来也有两种:
1> 增加栈内存的数目
2> 使用堆内存

3.JVM常用的调优参数有哪些?
详情看《堆内存调优》

4.内存快照如何抓取?怎么分析Dump文件?

1)获取jvm进程的dump快照文件

## 执行下面的jmap命令可以导出dump内存快照:
jmap -dump:live,format=b,file=dump.hprof pid

2)使用MAT分析内存快照
详情

5.谈谈JVM中,类加载器你的认识?
此问题主要是聊一下类加载器运行过程。

JVM体系结构

JVM体系

类加载器

类加载流程:

  1. 比如有一个 A.java ,当要使用A类时;
  2. 类加载器会先通过 双亲委派机制 找到对应的类:依次从启动类加载器(Bootstrap),扩展类加载器,应用程序类加载器中找,如果在启动类加载器找到,就不往下找了。如果都没有找到,会报classNotFund Exception的异常。
  3. 然后这个类成加载class文件,并且将class文件字节码内容加载到 内存 中,;
  4. 类装载器只负责class文件的加载, 至于它是否可以运行,则由 执行引擎 决定;

类加载

Java类加载的步骤(细节)

类生命周期

装载:把二进制形式的Java类型读入Java虚拟机中。

连接:把装载的二进制形式的类型数据合并到虚拟机的运行时状态中去。

验证:确保Java类型数据格式正确并且适合于Java虚拟机使用。

准备:负责为该类型分配它所需内存。

解析:把常量池中的符号引用转换为直接引用。

初始化:为类中的静态变量变量赋适当的初始值,执行静态代码块。

4种类加载器

  1. 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。(非常特殊: 无法被java程序直接引用)
  2. 扩展类加载器(extensions class loader)它用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java类。
  3. 系统类加载器(system class loader)也叫应用类加载器,它根据Java应用的类路径(CLASSPATH)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader0来获取它。
  4. 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。

双亲委派机制的好处:

  1. 因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
  2. 从安全角度考虑,如果不使用这种委托模式,我们自己定义一个Map来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患。
  3. 而双亲委托的方式,就可以避免这种情况。因为Map在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader不会在被加载一次。
  4. 通过双亲委派机制,类的加载永远都是从 启动类加载器开始,依次下放,保证你所写的代码,不会污染Java自带的源代码,所以出现了双亲委派机制,保证了 沙箱安全

JVM在搜索类的时候,又是如何判定两个class是相同的呢?

  1. JVM在判定两个class是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。只有两者同时满足的情况下,JVM才认为这两个class是相同的。就算两个class是同一份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同的class。

java栈(栈内存)

概念:

  1. 栈也叫栈内存,是一种只能从表的一端存取数据且遵循 “先进后出” 原则的线性存储结构。(类似像向枪弹盒里压下子弹,我们最后压进去的子弹,总是第一个打出)
    在这里插入图片描述
  2. 堆和栈区别?栈管运行,堆管存储。且8种基本类型的变量 + 对象的引用变量 + 实例方法都是在函数的 栈内存 中分配。而对象在 堆内存 中分配(存放所有new出来的对象)。

java栈生命周期:

  1. java栈主管java程序运行,是在线程创建时创建,结束时释放。

java实现栈

栈可以分为静态栈(数组实现)和动态栈(链表实现)

数组实现
public class ArrayStatck {

    private Object[] data; //数组
    private int top;//节点
    private int maxSize;//栈大小

    public ArrayStatck(int maxSize) {
        this.maxSize = maxSize;
        this.top = -1;
        data = new Object[maxSize];
    }
    public void addStack(Object value) throws Exception {
        if(top == maxSize-1)throw new Exception("栈满异常");
        data[++top] = value;
    }
    public void delStack()throws Exception{
        if(top == -1)throw  new Exception("栈空异常");
        Object value = data[top--];
    }
    public void getStack(){
        while (top != -1)
            System.out.println(""+data[top--]);
    }
    public static void main(String[] args) throws Exception {
        ArrayStatck arrayStatck=new ArrayStatck(10);
        for (int i=1;i<=10;i++){
            System.out.println(i);
            arrayStatck.addStack(i);
        }
        arrayStatck.delStack();
        arrayStatck.getStack();
    }
}
链表实现

略。分享链接: 小码过河.

栈存储什么?

栈帧中主要保存 3 类数据
① 本地变量 (Local Variables) : 入参和出参,以及方法内的变量;
② 栈操作 (Operand Stack) : 记录出栈和入栈的操作;(可理解为pc寄存器的指针)
③ 栈帧数据 (Frame Data) : 包括类文件、方法等。

堆内存

堆内存

概念:java堆中存放的是访问类元数据的地址

作用:存放所有new出来的对象

堆内存主要分成三个区域(新生区,老年区,元空间)

  1. 新生区
    类:诞生,成长甚至死亡的地方。(活的足够长的类,可能会进入老年区,但是大部分在新生区就玩完了。)
  2. 老年区
    新生区的对象满了,就放入老年区。统计上:绝大部分的对象都是临时对象,很少有对象能活到老年区。
  3. 元空间(java8之后 无永久区)
    这个区域是常驻内存的,用来存放JDK自身携带的Class对象,Interface原数据。简单理解为java运行时的环境或类信息。这个区域不存在垃圾回收!关闭JVM虚拟机就会释放这个区域的内存。
  4. 其他(伸缩区)
    概念:如果空间不足,在可变的范围之内扩大内存空间,当一段时间之后发现内存空间没有这么紧张的时候,再将可变空间进行释放。所以在整个调整过程之中

引入其他概念:

		元空间:逻辑上存在,物理上不存在,这也是将其称为“非堆的原因”!
		这个区域的空间会不会被占满呢?可以的。比如
			一个启动类,加载大量的第三方的jar包
			tomcat部署了太多的应用
			大量动态生成的反射类
		关于永久区的变化
			jdk1.6之前:永久代,常量池在方法区中
			jdk1.7:去永久代,常量池在堆中
			jdk1.8,:无永久代,常量池在元空间
		其他
			GC 垃圾回收,主要是在新生区和老年区
			内存爆满,会产生OOM(OutOfMemoryError)错误。
			JDK8以后,永久存储区的名字变为元空间。

堆内存调优

引入一个比较常见的问题,例如OOM 内存爆满问题。
解决方案:1.调整JVM的内存空间大小。

1 尝试扩大内存,看结果
	-Xms1024m -Xmx1024m -XX:+PrintGCDetails
	-Xms:设置初始分配大小,默认为物理内存的“1/64”
	-Xmx:最大分配内存,默认为物理内存的“1/4”
	-XX:+PrintGCDetails :输出详细的gc处理日志	Runtime.getRuntime().
	System.out.println("初始化内存:Total_memory="+Runtime.getRuntime().totalMemory()/(double)1024/1024+"M");
	System.out.println("默认最大内存:Max_memory="+Runtime.getRuntime().maxMemory()/(double)1024/1024+"M");
2 分析内存,查看问题,借助JProfiler
3.默认的情况下分配的内存是总内存的1/4;而初始化内存为“1/64”;
  那么也就是说整个内存的可变范围(伸缩区):123.0M ~ 1820.5M 之间,那么现在就可能造成程序性能下降;
  所以,我最好能让伸缩区的大小为0;即让 Max_memory 和 Total_memory 保持一致(-Xms 和 -Xmx 调成一样大小);

为啥说虚拟机的初始化内存和最大内存(-Xms和-Xmx)设置相同比较好?

  1. 这里的话,就得引入一个JVM垃圾回收概念了。当我们虚拟机初始化内存不足时,就会直接扩大内存?不是的,虚拟机初始化的内存不足时,会先调用JVM垃圾回收GC,GC操作是需要耗时的,而且Full GC会引起“Stop the World”,也就是说会引起线程停止,不可避免就会引起性能问题等等。
  2. 另外-Xmx和-Xms设置相等的话。避免在生产环境由于heap内存扩大或缩小导致应用停顿,降低延迟,同时避免每次垃圾回收完成后JVM重新分配内存。

Native 关键字 (Native Interface 本地接口)

概念:

  1. 在Java中是一个关键字,有声明,无实现。作用是融合不同的编程语言。native 方法需要在native method stack(本地方法栈) 中登记,在Execution Engine(执行引擎) 执行时加载本地方法库。

作用:

  1. 以线程为例,线程其实不属于Java的,其实它是属于操作系统底层的。Java中通过Thread类的start() 类启动一个线程。之后运行的是一个 private native void start0()方法。说明,线程方法是交由操作系统去处理的。

程序计程器

概念:也叫程序计数器。用于存储只想下一条指令的地址,即将要执行的指令代码。是最快的存储区。

每个线程都有一个程序计数器,是线程私有的,就是一个指针,只想方法区中的方法字节码。由执行引擎读取下一条指令。

方法区

概念:供各线程共享的运行时内存区域。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值