博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM
阅读量:2389 次
发布时间:2019-05-10

本文共 7228 字,大约阅读时间需要 24 分钟。

  • Java对象的创建过程
  • 类文件结构
  • 类加载的过程
  • 双亲委派模型

    文章目录

随着自己的不断学习,对之前所做的笔记会有不同的认识和理解,所以在不断学习的过程中,也要经常回顾自己的笔记

JMM内存模型

JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念 并不真实存在,它描述的是一组规则或规范通过规范定制了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式.

可见性(通知机制)

volatile关键字

class Juc02 {
volatile int i = 10; public void addTo1205() {
this.i = 1205;}}/** * JMM可见性 */public class Juc01 {
public static void main(String[] args) {
Juc02 juc02 = new Juc02(); new Thread(() ->{
System.out.println("我进入了Tread线程"); try {
Thread.sleep(3000); } catch (InterruptedException e) {
e.printStackTrace(); } juc02.addTo1205();//将10-->1205 System.out.println(Thread.currentThread().getName()+"\t更新数,数值为: "+juc02.i); },"AAA").start(); while(juc02.i ==10){
//System.out.println("我通知了main线程"); } System.out.println(Thread.currentThread().getName()+" mission is over"); }}

原子性

有序性

计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排,分为以下3中

在这里插入图片描述

1.单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致.

处理器在进行重新排序是必须要考虑指令之间的数据依赖性
2.多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程使用的变量能否保持一致性是无法确定的,结果无法预测

cpu|>内存>硬盘 三级缓存

谈谈JMM(并发)

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方成为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在 主内存,主内存是共享内存区域,所有线程都可访问, 但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作空间,然后对变量进行操作,操作完成再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存储存着主内存中的变量副本拷贝,因此不同的线程无法访问对方的工作内存,此案成间的通讯(传值) 必须通过主内存来完成,其简要访问过程如下图:

在这里插入图片描述

  • JMM关于同步规定:
    1.线程解锁前,必须把共享变量的值刷新回主内存
    2.线程加锁前,必须读取主内存的最新值到自己的工作内存
    3.加锁解锁是同一把锁

JVM

在这里插入图片描述

JVM体系结构概述

  • jvm是oracle公司的hotspot

JVM是运行在操作系统之上的,它与硬件没有直接的交互

在这里插入图片描述

JVM内存模型以及分区

在这里插入图片描述

类装载器ClassLoader

虚拟机自带的加载器

  • 启动类加载器(Bootstrap)C++
  • 扩展类加载器(Extension)Java
  • 应用程序类加载器(AppClassLoader)
    Java也叫系统类加载器,加载当前应用的classpath的所有类

用户自定义加载器

Java.lang.ClassLoader的子类,用户可以定制类的加载方式

双亲委派

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

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

沙箱机制

  • 保护机制String

在这里插入图片描述

Execution Engine
执行引擎负责解释命令,提交操作系统执行。
本地方法栈

Native Interface本地接口

  • 本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++程序,Java 诞生的时候是 C/C++横行的时候,要想立足,必须有调用 C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是 Native Method Stack中登记 native方法,在Execution Engine 执行时加载native libraies。

Native Method Stack

  • 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。
new thread().start()什么时候启动?   不确定,OS启动---native save0()
PC寄存器(pc计数器)

每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码

不会发生内存溢出(OutOfMemory=OOM)错误

  • 栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。

8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配。

方法调用自己时:`java.lang.StackOverflowError`

6.1 栈存储什么?

栈帧中主要保存3 类数据:
本地变量(Local Variables):输入参数和输出参数以及方法内的变量;
栈操作(Operand Stack):记录出栈、入栈的操作;
栈帧数据(Frame Data):包括类文件、方法等等。

Method Area 方法区

供各线程共享的运行时内存区域。它存储了每一个类的结构信息,例如模板静态的东西,运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容。上面讲的是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代(PermGen space)和元空间(Metaspace)。

But 实例变量存在堆内存中,和方法区(静态变量)无关

加载代码

静态>构造块>构造方法

heap 堆体系结构概述

一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:

Young Generation Space 新生区 Young/New
Tenure generation space 养老区 Old/ Tenure
Permanent Space 永久区 Perm

堆内存逻辑上分为三部分:新生+养老+永久

在这里插入图片描述

新生区

新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace) ,所有的类都是在伊甸区被new出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。若幸存 0区也满了,再对该区进行垃圾回收,然后移动到 1 区。那如果1 区也满了呢?再移动到养老区。若养老区也满了,那么这个时候将产生MajorGC(FullGC),进行养老区的内存清理。若养老区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。

如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:

(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。
在这里插入图片描述

MinorGC的过程(复制->清空->互换)

  • 1:eden、SurvivorFrom 复制到 SurvivorTo,年龄+1

    首先,当Eden区满的时候会触发第一次GC,把还活着的对象拷贝到SurvivorFrom区,当Eden区再次触发GC的时候会扫描Eden区和From区域,对这两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域(如果有对象的年龄已经达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1

  • 2:清空 eden、SurvivorFrom

    然后,清空Eden和SurvivorFrom中的对象,也即复制之后有交换,谁空谁是to

  • 3:SurvivorTo和 SurvivorFrom 互换

    最后,SurvivorTo和SurvivorFrom互换,原SurvivorTo成为下一次GC时的SurvivorFrom区。部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代

养老区

实际而言,方法区(Method Area)和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。

对于HotSpot虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)” ,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口interface)的一个实现,jdk1.7的版本中,已经将原本放在永久代的字符串常量池移走。

在这里插入图片描述

永久区(java7之前有)
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。

堆参数调优入门

JVM垃圾收集(Java Garbage Collection )

调新生代和养老代 无法修改永久代
在这里插入图片描述
在这里插入图片描述

  • 在Java8中,永久代已经被移除,被一个称为元空间的区域所取代。元空间的本质和永久代类似。

  • 元空间与永久代之间最大的区别在于:

    永久代使用的JVM的堆内存,但是java8以后的元空间并不在虚拟机中而是使用本机物理内存。

  • 因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入 java 堆中,这样可以加载多少类的元数据就不再由MaxPermSize 控制, 而由系统的实际可用空间来控制。

堆内存调优

内存调优:物理上堆内存–>在Runconf/VM改参数

-Xms1024m -Xmx1024m -XX:+PrintGCDetails
在这里插入图片描述

public static void main(String[] args){
//返回 Java 虚拟机试图使用的最大内存量。long maxMemory = Runtime.getRuntime().maxMemory() ;//返回 Java 虚拟机中的内存总量。long totalMemory = Runtime.getRuntime().totalMemory() ;System.out.println("MAX_MEMORY = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");System.out.println("TOTAL_MEMORY = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");}

JVM 内存不够用了,没有空闲内存,并且垃圾收集器也无法提供更多内存。

1.死循环会发生oom

String str = "comeOOM" ;while(true) {
str += str + new Random().nextInt(88888888) + new Random().nextInt(999999999) ;}

2.参数设置过小会发生oom

  • VM参数:-Xms8m -Xmx8m -XX:+PrintGCDetails

在这里插入图片描述

GC

1.JVM内存模型以及分区,需要详细到每个区放什么

2.堆里面的分区:Eden,survival from to,老年代,各自的特点。
3.GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方
4.Minor GC与Full GC分别在什么时候发生

GC是什么?

垃圾收集(分代收集算法)

当新生区或者养老区快满时发生GC

  • 次数上频繁收集Young区
  • 次数上较少收集Old区
  • 基本不动元空间

因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)

Minor GC和Full GC的区别

  • 普通GC(minor GC):只针对新生代区域的GC,指发生在新生代的垃圾收集动作,因为大多数Java对象存活率都不高,所以Minor GC非常频繁,一般回收速度也比较快。
  • 全局GC(major GC or Full GC):指发生在老年代的垃圾收集动作,出现了Major GC,经常会伴随至少一次的Minor GC(但并不是绝对的)。Major GC的速度一般要比Minor GC慢上10倍以上

引用计数算法(淘汰)

  • 每次对对象赋值时均要维护引用计数器,且计数器本身也有一定的消耗;
  • 较难处理循环引用

复制算法(Copying)

  • HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1:1,一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。因为年轻代中的对象基本都是朝生夕死的(90%以上),所以在年轻代的垃圾回收算法(Minor GC)使用的是复制算法
  • 复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。浪费2倍内存,效率高复制算法不会产生内存碎片。
    在这里插入图片描述

标记清除(Mark-Sweep)

老年代一般是由标记清除或者是标记清除与标记整理的混合实现

在这里插入图片描述
主要进行两项工作,第一项则是标记,第二项则是清除。
标记:从引用根节点开始标记遍历所有的GC Roots, 先标记出要回收的对象。
清除:遍历整个堆,把标记的对象清除。
缺点:此算法需要暂停整个应用,会产生内存碎片

标记压缩(Mark-Compact)

在这里插入图片描述

费时,占用OS资源,无碎片

标记清除压缩

在这里插入图片描述

  • 以hotspot中的CMS(Concurrent Mark Sweep)回收器为例,CMS是基于Mark-Sweep实现的,对于对像的回收效率很高,而对于碎片问题,CMS采用基于Mark-Compact算法的Serial Old回收器做为补偿措施:当内存回收不佳(碎片导致的Concurrent Mode Failure时),将采用Serial Old执行Full GC以达到对老年代内存的整理。
  1. JVM中的内存是怎么划分的

  2. 说一下对象创建过程中的内存分配

  3. 对象被访问的时候是怎么被找到的

  4. 内存分配与垃圾回收

  5. JVM如何判定一个对象是否应该被回收

    //1.引用计数算法2.可达性分析(Reachability Analysis)算法

  6. JVM垃圾回收算法有哪些?

  7. JVM的垃圾收集器? (重点CMS收集器)

  8. JVM常用内存调优命令

  9. JDK8中在内存管理上的变化

  10. Java中的类加载机制有了解吗

转载地址:http://kvxab.baihongyu.com/

你可能感兴趣的文章
ajax XMLHttpRequest五步使用法
查看>>
ajax跨域和js跨域解决方案 .
查看>>
如何用Squid来实现Ajax跨域代理
查看>>
APEX的安装
查看>>
Metasploit和armitage整合教程
查看>>
使用安全json parser防止json注入
查看>>
所有从非官方网站下载的putty和WinSCP都有后门(附清理方式)
查看>>
PHP 5.2.12 / 5.3.1 safe_mode / open_basedir Bypass
查看>>
Metasploit攻击Oracle的环境搭建
查看>>
信息安全合规性产品
查看>>
google-gruyere web2.0漏洞学习平台 =w=~
查看>>
Preventing Cross-site Scripting Attacks
查看>>
WASC Distributed Web Honeypots Project Update
查看>>
安装pydev到eclipse
查看>>
[WAF]apache和modsecurity的安装
查看>>
写给换工作和找工作的同学
查看>>
Island Hopping the SpiderLabs Way
查看>>
Top Ten Web Protection Techniques of 2011
查看>>
Faster Blind MySQL Injection Using Bit Shifting
查看>>
Safely Dumping Hashes from Live Domain Controllers
查看>>