JVM内存模型

一、JVM概述

        1、JVM简介

       JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际计算机上仿真模拟各种计算机功能来实现的。

        2、JVM的作用

        Java中所有的类,必须被装载到JVM中才能运行,这个装载工作是由JVM中的类装载器完成的,.class这个类型可以在虚拟机运行,但不是直接和操作系统交互,需要JVM解释给操作系统,解释的时候需要Java类库,这样就能和操作系统交互。

        3、JVM的内存模型

二.类加载器

        1.类加载器的作用

        将class字节码内容加载到内存中,并将这些静态数据转换成方法区运行时数据结构,然后在堆中形成代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。类装载器所做的工作实质是把类文件从硬盘读取到内存中。

        2.加载器的类型

        引导类加载器(Bootstrap ClassLoader):用c++编写,是JVM自带的类加载器,负责java平台核心库,用来装载核心类库,该加载器无法直接获取。

        拓展类加载器(Extension ClassLoader):负责jre/lib/ext目录下的jar包或 -D java.ext.dirs 指定下的jar包装入工作库。

         系统类加载器(Application ClassLoader):负责java-classpath或者 -D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。

        自定义类加载器(Custom ClassLoader):由开发人员自己定义。

        3.双亲委派机制的运行过程
                ① 类加载器收到类加载的请求。

                ② 将这个请求委托给父类加载器去完成,一直向上委托,直到引导类加载器。

                ③ 引导类加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常,通知子加载器进行加载,向下加载。

                ④ 重复步骤③。

        4、双亲委派模型

                当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

                采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个 Object 对象。

         优点: 保证类加载的安全性,不管那个类被加载,都会被委托给引导类加载器,只有类加载器不能加载,才会让子加载器加载,这样保证最后得到的对象都是同样的一个。
         缺点: 子加载器可以使用父加载器加载的类,而父加载器不能使用子加载器加载的类。

        5.为什么要破坏双亲委派机制

            原因:

            子加载器可以使用父加载器加载的类,而父加载器不能使用子加载器加载的类。
            举例: 使用JDBC连接数据库,需要用到 com.mysql.jdbc.Driver和DriverManager类。然而DriverManager被引导类加载器所加载,而com.mysql.jdbc.Driver被当前调用者的加载器加载,使用引导类加载器加载不到,所以要打破双亲委派机制。

        6.破坏双亲委派机制的方式

             (1) 自定义类加载器,重写loadclss()方法。
             (2) 使用线程上下文类(ServiceLoader:使父加载器可以加载子加载器的类)。

三、JVM内存模型

        JVM内存模型可以分为两个部分,如下图所示,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。

         1.方法区(线程共享)

        方法区存储的是:常量池、静态变量(static)以及方法信息(修饰符、方法名、返回值、参数等)、类信息(类变量)等

        2.堆(线程共享)

       概念:堆是线程共享的内存区域,它是虚拟机管理内存中最大的一块。

堆储存的是:实例对象。
       比如 A a1 = new A();a1就是实例对象。A a2;a2就是类对象。

        堆的具体示意图(jdk8及之后)

GC主要在新生区(伊甸园区)、老年区
    新生区(伊甸园区(对象都是在这个区new出来的)、幸存区to、幸存区from:幸存区位置会互相交换,谁空谁是to)
    老年区
    永久区:存储的是java的运行环境或类信息,这个区域不存在垃圾回收,关闭jvm就会释放内存
            一个启动类加载大量的jar包。tomcat部署太多应用。内存满了就oom
            jdk1.6之前:永久代,常量池是在方法区
            jdk1.7去永久代,常量池在堆中
            jdk1.8之后:无永久代,常量池在元空间中

         3.栈(虚拟机栈-线程隔离)

         概念:又名堆栈,主管程序运行,生命周期和线程同步,线程结束,栈内存就释放了。不存在垃圾回收问题。

         虚拟机栈储存的是:8大基本类型 + 对象引用 + 实例方法

        栈的具体示意图        

         4.栈(本地方法栈-线程隔离)

        本地方法栈储存的是:本地接口库里调用的方法,就是java里面native关键字修饰的方法。
        凡是带native关键字的,说明java的作用范围达不到了,回去调用底层c/c++语言的库,首先会进入本地方法栈,然后到本地方法接口。  

        5.程序计数器(线程隔离)

        每个线程启动是都会创建一个程序计数器,保存的是正在执行的jvm指令,程序计数器总是指向下一条将被执行指令的地址。生命周期与线程的生命周期保持一致。

        6.OutOfMemoryError内存溢出和StackOverFlowError栈溢出及解决方法

            不论是内存溢出还是栈溢出,简单来说就是你放的太多了空间不够了,溢出来了,这样就比较好理解了。

                (1) OutOfMemoryError内存溢出(OOM)

                原因:①是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。
                   ②由于长期保持某些资源的引用,垃圾回收器无法回收它,从而使该资源不能够及时释放,也称为内存泄露。

                解决:①设置JVM的堆参数( -Xmx:JVM最大内存 -Xms:启动初始内存 -Xmn:新生代大小 -Xss:每个线程虚拟机栈及堆栈的大小 ) 例如:-Xms1024m -Xmx1024m -Xmn512m -Xss5m。
                   ②分析内存,看一下那个地方出现了问题(专业工具:Jprofiler,MAT)分析Dump内存文件, 快速定位内存泄漏,怎么查找dump文件,直接找到文件的文件夹打开获得大的对象。

                制造堆溢出:一直死循环new对象。

  • JVM调优命令
    • –Xms:JVM初始分配的堆内存,默认是物理内存的1/64。
    • –Xmx:JVM最大允许分配的堆内存,默认是物理内存的1/4。
    • –Xmn:堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn。
    • –Xss:规定了每个线程虚拟机栈及堆栈的大小,一般情况下,256k是足够的,此配置将会影响此进程中并发线程数的大小。
                (2) StackOverFlowError栈溢出

                 原因:线程请求分配的栈容量超过Java虚拟机栈允许的最大容量。

                 解决:①修改代码 ②增加线程堆栈大小(-Xss)。

                 制造栈溢出:一直死循环调用方法就行(写个方法让它疯狂调用自己)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白橘大大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值