JVM初步了解

序章 JVM探究

  • 对JVM的理解?

  • java文件经过javac的编译形成class文件,进入JVM虚拟机运行

  • 运用条件模式

  • java8虚拟机和之前的变化?

  • OOM?

  • 栈溢出StackOverFlowError?怎么分析

  • JVM常用调优参数有哪些?

  • 扩大运行时内存

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

  • JVM中,类加载器你的认识?

  • rt-jar ext application

问题:

  1. JVM的位置

  1. JVM的体系结构

  1. 类加载器

  1. 双亲委派机制

  1. 沙箱安全机制

  1. Native

  1. PC寄存器

  1. 方法区

  1. 三种JVM

  1. 新生区、老年区

  1. 永久区

  1. 堆内存调优

  1. GC 垃圾回收器

  1. 常用算法:复制、标记、标记压缩、标记清除

  1. JMM

  1. 总结

一、JVM的位置

  • JVM(由C编写)运行在操作系统之上,互相交互

  • java编译后运行在JVM上

二、JVM的体系结构

三、类加载器

ClassLoader作用:加载Class文件

new 对象:将抽象类变为具体实例;

实例引用在栈中(数据存放地址);数据(具体信息)放在堆中

类是模板,抽象的;对象是具体的

不同对象,哈希值一定要不一样;同一对象,哈希值一定一样;哈希值一样,不一定是同一对象(哈希值通过计算,存在巧合)

  1. 虚拟机自带加载器;

  1. 启动类(根)加载器BootStrap;

  1. 扩展类加载器 ExtClassLoader;是AppClassLoader的父类,在扩展包 \jre\lib\ext

  1. ExtClassLoader的父类java获取不到,为rt.jar

  1. 应用程序(系统)加载器 AppClassLoader

四、双亲委派机制

  • 安全机制(防止故意修改上层内容,例:java.lang):从下层向上查找,最上层没有,再向下,直到找到最开始出现的位置

  • AppClassLoader--》ExtClassLoader--》BootStrap

  • 类加载器收到类加载的请求;该请求委托给父类加载器完成,一直向上委托,直到启动类加载器;启动加载器检查是否能加载当前类,能,结束,使用当前加载器;否则,抛出异常,通知子加载器加载,重复该步骤;

  • 经典错误:Class Not Found

  • Null:java调用不到,底层由C和C++编写

  • java=C++-- 由C++去掉繁琐东西,指针和内存管理

五、沙箱安全机制(了解)

java安全模型核心就是java沙箱(sandbox),沙箱是一个限制程序运行的环境。沙箱机制将java代码限定在虚拟机的指定运行范围中,严格限制代码对本地资源访问,保证对代码的有效隔离,防止对本地系统的破坏。

沙箱主要限制系统资源访问!包括CPU、内存、系统文件、网络,根据对资源访问的限制,有不同的沙箱级别。所有java程序运行都可以指定沙箱,定制安全策略,

JDK1.0安全模型,java将执行程序分成本地代码和远程代码;本地代码默认可信任,访问一切本地资源;远程代码,不受信任,安全依赖沙箱机制。

JDK1.1安全模型,1.0严格的安全机制为程序功能扩展带来障碍,如远程代码访问本地文件无法实现;因此1.1改进安全机制,增加了安全策略,允许用户指定代码对本地资源的访问权限

JDK1.2安全模型,再次改进,增加了代码签名,不论本地代码还是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间(不同的权限组和沙箱),实现差异化代码执行权限控制。

JDK1.6安全模型,最新的安全机制实现,引入域(Domain)。JVM将所有代码加载到不同的系统和应用域,系统域部分专门负责与关键资源交互;各个应用域部分通过系统域部分代理来对各种需要的资源进行访问。不同的受保护域(Protected Domain),对应不一样的权限(Permission),对于不同域中的类文件,就具有了当前域的全部权限。(windows系统)(jvm内存溢出行为会被限制进行)

组成沙箱的基本组件:

  • 字节码校验器(bytecode verifier):确保java类文件遵循java语言规范。帮助java程序实现内存保护。并不是所有类文件都会经过字节码校验,如核心类(java、javax)

  • 类装载器(class loader):在三个方面对java沙箱起作用,

  • 防止恶意代码干涉善意代码

  • 守护被信任的类库边界

  • 将代码归入保护域,确定代码可以进行的操作(如外部代码不可以调c)

  • 采用双亲委派机制:严格通过包来区分访问区域,外层恶意类通过内置代码也无法获得权限访问到内层类,无法破坏代码

  • 存取控制器(access controller):可以控制核心API对操作系统的存取权限,该控制策略设定可以由用户指定

  • 安全管理器(security manager):核心API和操作系统间主要接口,实现权限控制,比存取控制器优先级高

  • 安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:

  • 安全提供者

  • 消息摘要

  • 数字签名 keytools

  • 加密

  • 鉴别

六、Native(掌握)

  • native:凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层C语言的库!

  • 进入本地方法栈

  • 调用本地方法接口:JNI---- Java Native Interface

  • JNI作用:扩展java的使用,融合不同的编程语言为java所用。最初目的:融合C、C++(当初二者横行)

  • 内存区域中专门开辟了一块标记区域:Native Method Stack,登记native方法

  • 最终执行的时候,加载本地方法库中的方法需要JNI

  • 调用其他语言的方法:

  • 通过接口调用方法:Socket..WebService..http

  • Native应用实例:(企业级应用中较少)

  • java程序驱动打印机

  • 管理系统

  • Robot

  • 混合语言实例:

  • 球球爱心网:--》输入(PHP)--》NodeJS--》Socket--》C++--》刷爱心

七、PC寄存器

程序计数器:Program Counter Register

每个线程都有一个程序计数器,是线程私有的,本质是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,即将要执行的指令代码),执行引擎读取下一条指令是一个非常小的内存空间,可忽略不计

八、方法区

Method Area :方法区

方法区被所有线程共享,所有字段和方法字节码,以及一些特殊的方法,如构造函数,接口代码也在此定义,所有定义的方法的信息都保存在此区域,属于共享空间

静态变量static、常量final、类信息Class模板(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关

public class Test{
    //运行时的常量池存在方法区中
    private int a;
    private String name = "qing";
    
    public static void main(String[] args){
        Test test = new Test();
        //实例变量存在堆内存中
        test.a=1;
        test.name="hihihi";
    }
}

九、栈:数据结构

程序=数据结构+算法 (持续学习)

程序=框架+业务逻辑(仅吃饭)

  • 栈:先进后出、后进先出,如:弹夹压子弹;

  • 栈内存,主管程序的运行,生命周期和线程同步;

  • 线程结束,栈内存释放,对于栈类来说,不存在垃圾回收问题;

  • 一旦线程结束,栈便Over;

  • 栈存放内容:

  • 8大基本类型+对象引用+实例方法

  • 队列:先进先出(FIFO:First Input First Output)

  • 喝多了吐,栈;吃多了拉,队列;

1.为什么main()方法先执行,最后结束

main方法执行后最先被压入栈内,随后是其他方法,栈是先进后出,当其他方法弹出栈后,main才会弹出,程序执行结束。

2.栈内存溢出:StackOverflowError

死循环中,方法被无限的压入栈中,无法弹出,会超出栈的存储极限,造成错误。

例:A调B,B调A;A和B轮番被压入栈中ABABAB;该情况无法跳出结束;

3.栈运行原理:栈帧

栈满了:StackOverflowError

4.栈+堆+方法区 的交互关系

java本质是值传递

5.栈中存的内容,怎么存?

待完善

6.画出一个对象实例化的过程在内存中:

待完善

十、三种JVM

  • sun公司: HotSpot(学习此版本) Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

  • BEA JRockit

  • IBM J9 VM:平台硬件系统绑定,有局限

十一、堆

Heap:一个JVM只有一个堆内存堆内存的大小是可调节

程序运行后,可以调节堆内存参数;

类加载器读取类文件后,堆中存放类、方法、常量、变量,保存所有引用对象的真实对象;

堆内存中细分三个区域:

  • 新生区(伊甸园区) Young/New

  • 养老区 old

  • 永久区 Perm

GC垃圾回收,主要是在伊甸园区和养老区;

假设内存满了,OOM,堆内存不够(无限new新生区);

JDK8以后,永久区Perm变更为元空间

十二、新生区、老年区

  • 新生区:

  • 类诞生和成长的地方,甚至死亡;

  • 伊甸园:所有对象都是在伊甸园区new出来的

  • 每次GC后,都会将活的对象移到幸存区中;GC结束后,伊甸园是空的

  • 幸存区(0,1):

  • 伊甸园存储对象满,启动轻GC,存活对象(还存在引用)(包括1区中存活的)进入幸存0区

  • 0和1区之间动态互换(from to);每次清理都会换位置;

  • 谁空谁是to;二者必有一空;

  • 养老区:

  • 养老区全满后,触发重GC,新生区和养老区都进行清理,存活者进入养老区

  • 一个对象经历15次GC(默认值)还存活,进入养老区

  • -XX:MaxTenuringThreshold=5

  • 新生区和养老区都满后,触发OOM

  • 99%对象都是临时对象;

十三、永久区

  • jdk1.6之前:永久代,常量池在方法区

  • jdk1.7:永久代,退化,去永久代,常量池在堆中

  • jdk1.8之后:无永久代,常量池在元空间;

该区域常驻内存,用来存放JDK自身携带的Class对象,Interface元数据,存储的是java运行时的一些环境或类信息,该区域不存在垃圾回收!关闭VM虚拟就会释放这个区域的内存。

一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类;不断加载,直到内存满,出现OOM;

十四、堆内存调优

  • OOM(常见问题):

  • 尝试扩大堆内存看结果

  • 分析内存,看问题具体位置(专业工具)

  • -Xms1024m -Xmx1024m -XX:+PrintGCDetails

  • -Xms8m -Xmx8m -XX:+PrintGCDetails

package com.yanfeng;

/**
 * OOM分析:
 * 扩大堆内存
 */
public class Demo {
    public static void main(String[] args) {
        //返回虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory();
        //返回jvm的总内存
        long total = Runtime.getRuntime().totalMemory();

        //max=1884815360字节	1797.5MB
        System.out.println("max="+max+"字节\t"+(max/(double)1024/1024)+"MB");
        //total=128974848字节	123.0MB
        System.out.println("total="+total+"字节\t"+(total/(double)1024/1024)+"MB");

        //默认情况:分配总内存 是电脑内存的1/4,初始化内存:1/64

        //-Xms1024m -Xmx1024m -XX:+PrintGCDetails

        /**
         max=1029177344字节	981.5MB
         total=1029177344字节	981.5MB
         Heap
         PSYoungGen      total 305664K, used 15729K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
         eden space 262144K, 6% used [0x00000000eab00000,0x00000000eba5c420,0x00000000fab00000)
         from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
         to   space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
         ParOldGen       total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
         object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
         Metaspace       used 3162K, capacity 4496K, committed 4864K, reserved 1056768K
         class space    used 344K, capacity 388K, committed 512K, reserved 1048576K
         */

        // 新生区305664K+ 永久区699392K = 981.5MB
    }
}

十五、使用Jprofiler工具分析OOM原因

1.项目中的OOM问题,排除方式和错误原因

  • 找到代码出错行数:内存快照分析工具,MAT(eclipce集成工具)、Jprofiler

  • Debug,分析代码

2.MAT,Jprofiler作用:

  • 分析Dump内存文件,快速定位内存泄漏;

  • 获得堆中数据;

  • 获得大的对象;

3.Jprofiler安装

  • idea下载插件:Jprofiler

  • 安装Jprofiler软件

  • 重启idea后,在settings--》Tools--》Jprofiler

  • 绑定E:\Program Files\jprofiler9\bin\jprofiler.exe

存在OOM问题的类,设置-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError,生成问题汇总文件(在工程项目文件夹下),通过JProfile软件进行精确的问题定位;

主要看Biggest Objects中的占用信息;快速定位问题出现处

// -Xms 设置初始化内存分配大小,1/64
// -Xmx 设置最大分配内存,默认1/4
// -XX:+PrintGCDetails //打印GC垃圾回收信息
// -XX:+HeapDumpOnOutOfMemoryError //oom DUMP
//-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

public class Demo01 {
    byte[] array = new byte[1*1024*1024];//1m

    public static void main(String[] args) {
        ArrayList<Demo01> list = new ArrayList<>();
        int count = 0;

        try {
            while (true){
                list.add(new Demo01());//问题所在
                count++;
            }
        } catch (Exception e) {
            System.out.println("count="+count);
            e.printStackTrace();
        }
    }
}

十六、GC 垃圾回收器

JVM进行GC时,并不是对三个区域统一回收,大部分时候,回收都是新生代。

轻GC、重GC(全局GC)

1.引用计数法(JVM中一般不采用)

每个对象分配一个计数器,计算引用次数,没有引用的对象会被清理出去;计数器本身也有消耗,不高效

2.GC之复制算法(年轻区使用)

  • 原理:

  • 每次GC清理eden和from,会将还存活的对象复制到to,清理eden和from,最后from和to区互换;

  • 好处:没有内存碎片

  • 坏处:浪费内存空间,有一般空间(to区)是空的

  • 极端情况:100%对象存活;内存地址全部重做,消耗过大

  • 复制算法最佳使用场景:对象存活度较低时:新生区

3.GC之标记压缩清除算法

3.1清除算法
  • 原理:

  • 扫描所有对象,标记存活对象;

  • 扫描对象,清除未标记对象;

  • 优点:

  • 不需要额外空间;

  • 缺点:

  • 两次扫描,严重浪费时间(2次扫描);

  • 会产生内存碎片,查找对象需要hash定位(存储位置零散)

3.2标记压缩

压缩:防止内存碎片产生,再次扫描,向一端移动存活的对象;

十七、总结

  • 内存效率:复制算法(空间需求大)》标记清除算法》标记压缩算法(时间复杂度)

  • 内存整齐度:复制算法=标记压缩算法》标记清除算法

  • 内存利用率:标记压缩算法=标记清除算法》复制算法

1.是否有最优算法?

否,没有最好的算法,只有最合适的---》GC:分代收集算法

  • 年轻代:

  • 存活率低

  • 复制算法

  • 老年代

  • 区域大:存活率低

  • 标记清除(内存碎片不是太多)+标记压缩 混合实现

  • 按线索学习jvm

二十、GC题目

  • JVM的内存模型和分区,详细到每个区放什么?

  • 堆里面分区有什么?说说他们特点!

  • eden

  • form to

  • 老年区

  • GC算法有哪些?怎么用

  • 标记清除法,标记整理(压缩),复制算法,引用计数器

  • 轻GC和重GC分别在什么时候发生?

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值