zoukankan      html  css  js  c++  java
  • JAVA复习笔记(面试向)

    JVM 内存管理

    内存区域

    JVM内存分为线程私有的和公共区域,

    线程私有的由程序计数器,JAVA虚拟机栈和本地方法栈构成

    公共区域分为JAVA堆和方法区,1.8之前方法区叫做永生代,之后改为元空间

    程序计数器

    程序计数器:可以看作当前线程执行的字节码的行号指示器,字节码解释器就是通过改变程序计数器,选取要运行的下一条字节码。

    程序计数器的两个功能:

    • 记录线程运行状态,在发生上下文切换的的时候记录程序的状态,以便恢复到原来的状态

    • 控制程序的执行

    程序计数器,线程私有,并且不会发生OOM

    JAVA虚拟机栈

    虚拟机栈:虚拟机栈描述方法执行的内存模型,存放局部变量表,操作数栈,动态链接和方法出口,一个方法调用到执行完成,就是栈帧在虚拟机栈中从入栈到出栈。

    局部变量表:存在局部变量和对象引用

    存在StackOverFlowError和OOM

    本地方法栈

    本地方法栈,和JAVA虚拟机栈基本相同,但是执行的是本地方法。

    虚拟机规范中未定义具体实现,HotSpot中和JAVA虚拟机栈合二为一

    存在StackOverFlowError和OOM

    JAVA堆

    JAVA堆用来存放实例对象的内存区域,几乎所有对象都分配在堆

    基于垃圾分代回收假说,为了更好的回收垃圾,所以需要对JAVA堆分代。

    经典分代:

    • 新生代(Eden,from survivor 和 to survivor)

    • 老年代

    其他分代:

    将内存区域分为多个region,每个region既可以作为老年代也可以作为新生代

    方法区

    方法区存放已经加载的类信息、常量和静态变量。

    类的信息包括:版本、字段、方法、接口等 还有常量池表

    1.7以后将常量和静态变量移出了

    1.8以后改作元空间

    方法区也存在垃圾回收:常量回收和类卸载

    运行时常量池

    方法区的一部分,当类被加载后,类的常量信息存放在运行时常量池表

    常量池包括:

    • 字面量:字符串值、基本数据类型、final的常量,其他

    • 符号引用:类和结构的名字,字段和描述符,方法名称和描述符

    字面量就是值,比如int i = 1;String s =“abc”;1和“abc”就是字面量,s就是符号引用

    静态常量池的概念:静态常量池就是在类没被加载的时候,.class文件中的常量池

    但是常量池不是必须是编译时期产生的,比如string.intern()方法就可以将字符串对象的字面量放入字符串常量池。

    字符串常量池

    存放字符串常量的地方,1.8以后在元空间

    直接内存

    native的方法直接在JVM以外的内存区域分配内存

    内存中的对象

    一个对象创建

    对象创建分五步,

    类加载检查,内存分配、初始化、设置对象头、执行init方法

    • 类加载检查:检查对象所属的类是否已经被加载了,如果被加载了,就通过常量池的符号引用在方法区找到这个类。

    • 内存分配:为对象在JAVA堆上分配空间。指针碰撞和空闲列表,对象整理复制和清除。同步性:CAS操作失败重试或者本地线程分配缓冲

    • 初始化:赋0值

    • 设置对象头:对对象必要的设置,比如属于哪个类、hash码、分代年龄还有是否启用偏向锁等

    • 执行init,按照程序猿构造的方法,对对象初始化

    对象内存布局

    对象在堆内存中,主要分为三个部分:对象头、实例数据、对齐填充

    对象头:

    • 对象自身运行数据:hashacode、分代、锁状态(Mark word记录)

    • 对象指针:指向所属类

    对象访问方式

    句柄和直接访问

    句柄:JAVA栈的本地变量表中指向JAVA堆中一个句柄池中的指针,句柄池中指针指向对象实例数据和类型类型数据的指针

    直接方法:JAVA栈的本地变量表中指向JAVA堆中实例对象,对象实例数据有指针指向对象类型数据

    好处:直接指针,快,节省一次开销,句柄,对象移动,只需要改变句柄。hotspot用直接访问

    一个String对象

    new一个string对象会产生两个string对象,一个字符串常量在常量池一个堆中string对象

    不用new直接拼接,会在堆生成一个对象,然后引用指向常量池,实际上是调用了stringbuilder拼接,然后返回一个对象。

    不用new直接写,在常量池,然后引用这个对象

    string.intern:第一次在常量池生成一个对象的拷贝,第二次不操作

    垃圾回收

    引用计数算法和可达性分析算法

    判断一个对象是否存活。

    引用计数:有一个引用就+1,消失就-1,为0就死亡。hotspot没用,用的是可达性分析算法

    可达性分析:从一系列GC Roots出发,按照引用关系向下搜索,完成引用链,不可达的对象就是死亡对象。

    GR roots:

    • 虚拟机栈中引用

    • 方法区中静态属性应用的对象

    • 常量区引用的对象

    • 本地方法区native 方法引用的对象

    • 锁持有的对象

    • 虚拟机内部的对象还有类加载器

    引用种类

    强引用:不会被回收

    弱引用:不够了被回收

    软引用:下一次被回收

    虚引用:不影响回收

    finalize()方法

    GC Roots不可达之后,进行一次标记,然后检查是否有finalize方法,有就执行一次,没有或者执行过了就可以执行回收。

    finalize方法可以让对象自救,比如在对象内引用上自己

    方法区回收

    同时满足:

    • 所有实例被回收

    • 加载器被回收

    • java.lang.Class对象没有被引用,不可以反射

    跨代引用

    不同年龄段的对象之间存在指向对方的引用。

    垃圾回收的时候如何处理?

    使用一个记忆集:一种从非回收区域,指向回收区域的的指针的抽象集合

    回收算法

    标记清除:效率不稳定、碎片化

    标记整理:开销很大

    标记复制:浪费一部分内存空间,如果按照1:1分配就会浪费50。hotspot也用的是这种,hotspot中新生代分为8:1:1,Eden和survivor,将一个Eden和survivor复制到另一个survivor。分配不下就内存担保机制。

    移动对象开销大,不移动对象分配大。从整个程序上看,移动比较划算。

    碎片化问题可以通过执行一段时间之后,标记整理一次。

    Hotspot实现细节

    GC roots枚举

    枚举出GC roots。如何找到引用对象?使用的是Oopmap(ordinary object pointer)普通对象指针。使用OopMap可以快速枚举gc roots,但是导致OopMap变化的指令很多,开销很大。

    所以只在特定的位置,生成OopMap,这些位置叫做安全点。

    安全点和安全区域

    安全点:所以程序只有执行到安全点的时候,才可以垃圾回收。

    安全点选择:不至于等待时间太长,也不至于太频繁。所以在循环跳转,方法调用等情况下设置安全点

    中断方式:抢断式和主动中断,要准备垃圾收集了就中断所有线程,不在安全点的就恢复执行到安全点,主动式设置标志位,发现标志位就主动执行到安全点中断。

    安全区域:安全点选择结局了执行中线程的引用变化问题,但是对于不执行的程序,使用安全区域。线程执行到安全区域就标识一下,出区域的时候检查垃圾回收中gcroot枚举是否完成。

    记忆集和卡表

    记忆集:一种从非回收区域,指向回收区域的的指针的抽象集合

    卡表:记忆集的一种实现,定义记录进度和内存映射关系,就是记录了某一块内存中是否有对象被另一块内存引用

    字长精度和对象精度,这俩精度更高

    写屏障

    维护卡表,保证引用关系变化的时候,卡表可以随之变化。

    并发的可达性分析

    由于和程序是并发执行的,防止引用关系变化,使用增量更新或者初始快照。

    变化导致的错误:

    A引用B,然后扫描A的时候,引用被删除,所以B不在引用链,然后之后扫描过的C引用了B

    增量更新:当扫过对象增加新的引用的时候,记录下来,结束后,以这些对象为root再扫一遍

    初始快照:删除正在扫描对象引用的时候,记下这些对象,结束后重新扫描一遍。

    CMS使用增量,G1使用快照

    经典垃圾收集器

    serial

    新生代串行收集器

    标记复制

    ParNew

    新生代并行收集器,serial并行版本

    标记复制

    CMS默认的新生代收集器

    parallel scanvge

    新生代并行收集器

    标记复制

    注重于可预测的停顿时间,可控制的吞吐量,对于暂停时间可以预测,吞吐量优先

    通过设置最大停顿时间和和吞吐量大小的参数,调节收集新生代大小,从而决定停顿时间

    还可以开启自适应调节

    serial old

    老年代序列收集器

    标记整理

    parallel old

    老年代并行收集器

    标记整理

    CMS

    四个阶段:初始阶段、并发标记、重新标记、并发清除

    初始阶段:GC roots枚举,停顿

    并发标记:可达性分析,查找引用链

    重新标记:并发标记的时候变化的引用关系重新标记,停顿

    并发清除:清除

    浮动垃圾和重新标记?

    重新标记的是引用链里已经有的对象,但是对象的位置发生了改变,而浮动垃圾是标记过程以后产生的垃圾,不在引用链里了。

    标记清除算法

    缺点:浮动垃圾、资源敏感、碎片化

    G1

    Garbage First 是面向局部的垃圾回收器,将内存区域分为不同的region,1.8后支持对类的卸载。

    四个阶段:初始阶段、并发标记、最终标记、筛选回收

    初始阶段:GC roots枚举,借用minor gc时间完成

    并发标记:可达性分析,查找引用链

    最终标记:SATB中的记录,再检查一遍

    筛选回收:按照能回收的空间回报回收region,直接将一个region中存活对象复制到另一个空region,不要求一次清理干净,不影响执行就可以

    SATB 原始快照算法

    TAMS指针,用于并发回收的时候分配对象

    缺点:内存占用太高,因为需要记忆集记录跨代引用

    局部上看标记复制,总体看是标记整理

    Hotspot虚拟机实战

    -Xms20M -Xmx20M -Xmn10M

    -printGCDetials等参数运行虚拟机

    证明:

    大对象直接进入老年代

    内存担保

    动态年龄判断:相同年龄所有对象大雨survivor一半

    垃圾回收触发条件

    minor gc:Eden满了

    full gc:至少伴随一次minor

    • 老年代满了

    • 调用system.gc()可能会清理

    • 空间担保失败

    • 1.7之前永久代不足

    • CMS GC过程中,分配新对象内存失败,直接全部暂停full gc一次

    CLASS文件结构

    魔术和版本

    常量池

    访问标志

    类、父类、接口索引

    字段表

    方法集合

    属性表:属性表记录,上面三个多一些特定场景,例如字段为常量

    类加载

    类加载机制:虚拟机从class文件,从读取、数据检验、转换分析、初始化,最终形成可以直接使用的JAVA类的过程

    类的生存周期:

    加载、验证、准备、解析、初始化、使用、卸载

    类加载器

    将.class文件加载到内存中,变成二进制流,一个class对应一个类加载器

    分类:

    • 启动类加载器,C++实现,虚拟机一部分

    • 其他所有类加载器,java.lang.classloader子类

    分类2:

    • 启动类加载器

    • 扩展类加载器:加载类库

    • 应用程序类加载器:加载应用程序

    双亲委派模型

    一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时才尝试自己加载。

    好处:避免重新加载和核心api被篡改

    加载

    加载阶段完成三件事:

    • 将二进制流加载入内存,并且可以通过一个类全名访问

    • 将字节流表示的静态数据结构,转化为方法区内运行时数据结构

    • 内存中生成一个java.lang.Class对象,作为方法区中类的各种数据访问入口。

    验证

    确保字节流包含的信息符合规范,不会危害虚拟机,因为二进制流不一定都是编译出来的。

    准备

    为类中变量也就是静态变量分配内存。不会赋值,初始化的时候才会赋值,现在为0

    1.8以后随着Class对象一起存放在JAVA堆

    解析

    将JAVA虚拟机中常量池中符号引用,替换为直接引用的过程。

    初始化

    按照程序猿的编写的代码,初始化变量和其他资源。

    一定要立即初始化

    • new

    • 反射调用reflect

    • 子类初始化时,如果父类没有,先初始化父类

    • 虚拟机启动,用户指定执行的主类

    • default

    虚拟机字节码执行

    虚拟机字节码执行,是在线程中进行的,方法是执行的基本单元

    栈帧是支持调用和执行的数据结构,执行一个方法,就将这个方法的栈帧压入虚拟机栈

    每一个栈帧,都包括:本地方法表、操作数栈、动态链接和方法返回地址

    只有栈顶的栈帧被执行。

    操作数栈

    方法执行的过程,就是各种字节码指令往操作数栈中写入和提取的内容

    动态链接

    栈帧中指向常量池中方法引用的调用过程。

    有些类加载的时候就已经转换成直接引用了,有些是运行时才变成直接引用的,这个过程叫做动态链接。

    方法调用

    确定方法的版本,因为存在多态嘛,和方法执行的调用不是一个含义

    解析和分派

    前端编译器、即时编译器、提前编译器

    前端编译器:编程成字节码,.java编程.class。

    即时编译器:一边执行一遍编译热点代码成机器码,运行时编译器JIT just in time

    提前编译器:直接编译成机器码,AOT ahead of time complier

    优缺点:

    • 即时编译消耗资源

    • 提前编译不能跨平台

    JAVA内存模型

    内存模型:对内存进行读写访问过程的抽象

    主要目的:定义程序中各种变量的访问规则,关注虚拟机如何吧变量值存储到内存,又如何从内存中取出。

    JAVA中分为主内存和工作内存,

    线程在工作内存执行,变量存储在主内存,通过SAVE和LOAD操作从主内存获取数据。

    内存之间操作

    lock 作用于主内存,锁定,线程独占

    unlock 作用于主内存,解锁

    read 作用于主内存,读出数据

    load 作用于工作内存,read出来的变量放入工作内存

    use 作用于工作内存,给执行引擎

    assign 作用于工作内存,执行引擎操作过的值给工作内存

    store 作用于工作内存,从工作内存读出,准备给主内存

    write 作用于主内存,放入主内存

    JAVA线程实现

    系统线程实现,用户线程实现和混合实现

    线程安全和锁优化

    线程安全有互斥同步和非阻塞同步

    互斥同步

    临界区、互斥量和信号量

    synchronized和重入锁

    区别:

    • 重入锁可以等待可中断:得不到可以申请放弃

    • 公平锁:按照申请顺序获得,sychronized不公平

    • 绑定多个条件:condition条件控制阻塞

    没有特殊需求,推荐synchronized,原因:

    • sychronized代码易读

    • lock需要finally中释放,不释放永久持有

    • 性能差不多

    非阻塞同步

    CAS指令的支持,完成比较和交换动作,原子性,比较变了吗,没有才执行,会有ABA问题。

    乐观锁:就是不断的尝试,可以就修改,不可以就不断尝试

    自旋锁和自适应自旋锁

    请求不到就等待,但是不切换,不释放资源,因为可能很快就可以获得资源了,减少切换的开销

    锁消除

    编译的时候,没有发现访问冲突,直接消除锁

    锁粗化

    同步代码块总是太小,频繁切换会很浪费资源,所以让同步代码块的范围更大

    轻量锁

    将资源标记与属于一个线程,其他资源访问的时候,先看看有没有人占用,有的话变成传统重量级锁。

    基于CAS操作,每次使用的时候CAS检查自己是否拥有。

    偏向锁

    CAS操作都不做了,获得偏向锁以后,每次都不申请了,直接使用,直到其他进程也要使用,升级成轻量或者重量级锁。

  • 相关阅读:
    201571030334/201571030323《小学四则运算练习软件软件需求说明》结对项目报告
    201571030334/201571030323实验三 软件工程结对项目
    201571030334 小学四则运算练习软件项目报告
    201571030334 快速通读教材《构建之法》后的五个疑惑
    个人学期总结
    201571030333/201571030132《小学四则运算练习软件需求说明》结对项目报告
    201571030333/201571030132《小学四则运算》结对项目报告
    201571030333 小学生四则运算软件的开发
    关于阅读《构建之法》的疑惑
    四则运算的调查问卷
  • 原文地址:https://www.cnblogs.com/CooperXia-847550730/p/12913734.html
Copyright © 2011-2022 走看看