Java知识(二)


1、内存中对象?

对象实例中主要包含三部分的内容,分别为对象头、示例数据和对齐填充padding。下面分别进行介绍。
1、对象头
对象头中的内容主要是运行时元数据和类型指针。其中运行时元数据主要存储的是以下六个内容:
1.哈希值

  1. GC分代年龄
  2. 锁状态标志
  3. 线程持有的锁
  4. 偏向线程ID
  5. 偏向时间戳
    所以我们知道,锁是在对象头中的。
    那么类型指针又是什么呢?
    实际上,类型指针是指向元数据类型的InstanceKlass,用来确定该对象所属的类型。比如通过getInstance()得到对象实例时就需要用到该指针。这里需要注意的是,不是所有对象都会保留该指针的。
    以上我们说的对象指的是自定义对象,如果是数组对象,那么还需要记录数据的长度。

2、AQS

抽象队列同步器。AQS通过一个被volatile修饰的int类型的成员变量表示同步状态(共享资源),然后使用cas对这个同步状态进行修改,从而保证线程安全。
如果被请求的共享资源空闲,则将当前的请求线程设置为有效的工作线程,然后将对应的共享资源设置为锁定状态。
如果共享资源被占用,则通过CLH同步队列将暂时用不到的线程封装成一个节点加入到队列中,同时在适当的时候对其进行阻塞和唤醒。

3、CAS

  1. Compare-And-Swap。是一条CPU并发原语。(原语:操作系统范畴,依赖硬件,不被中断)
  2. 功能是判断内存某个位置的值是否为预期值(Compare),是就更新(Swap),这个过程是原子的。
  3. 功能描述:
    a. 判断内存某个位置的值是否为预期值(Compare),是就更新(Swap),这个过程是原子的。
    b. cas有三个操作数,内存值V,旧预期值A,要更新的值B。仅当预期值A=内存值V时,才将内存值V修改为B,否则什么都不做。
  4. 自旋:比较并交换,直到比较成功
  5. 底层靠Unsafe类保证原子性。

4、ThreadLocal

ThreadLocal提供了一种方式,让在多线程环境下,每个线程都可以拥有自己私有的数据结构,进而减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。但因为其自身的实现机制,使用之后记得及时remove,避免内存泄漏。
每个线程都有一个threadLocals变量,该变量的数据结构是threadLocalMap,而map中的每个entry中key是threadLocal,value是我们需要存储的本地变量
因为threadLocalMap的中的key也就是threadLocal是弱引用,所以只要触发GC,key就会被回收,这样ThreadMap中的key就变成了null,但是value被Entry引用,Entry被ThreadLocalMap引用,
ThreadLocalMap被Thread引用,这也就说明了只要,线程不终止,value的值一直无法被回收,所以可能会出现内存泄漏的现象,所以使用完之后,要及时调用remove方法。

5、JVM内存管理

Java运行时数据区包含哪几个部分?哪些是共享的,哪些是私有的?有以下部分:
Java堆,方法区,Java虚拟机栈,本地方法栈,程序计数器。
在这里插入图片描述JVM包含两个子系统和两个组件。两个子系统:
1.类加载子系统:负责将字节码文件装载内存成为一个对象。
2. 执行引擎:包含解释器、JIT编译器、垃圾收集器(GC)
两个组件:
1.运行时数据区
2.本地接口

6、对象的创建

  1. 类加载检查
  2. 分配内存
  3. 初始化内存空间
  4. 设置对象头
  5. 执行构造函数

7、 对象访问定位

对象的访问的两种方式?区别和优缺点
Java通过栈上的reference数据来操作堆上的具体对象。对象访问的两种方式为句柄和直接指针
1、句柄方式
Java堆中将可能会划分除一块内存来作为句柄池,然后reference储存句柄池的地址,句柄中包含了指向对象实例数据的指针,和对象类型数据的指针。
2、直接指针
reference中放的是对象地址
3、优缺点
句柄方式比较稳定︰对象被移动时,只会改变句柄中的实例数据指针,不需要修改reference本身。直接指针的话,少了一次指针定位的开销,所以好处就是访问速度更快。

8、JVM之经典收集器—G1

全称Garbage-First,之前的收集器因为是直接将堆内存物理划分为新生代老年代
所以需要来两种收集器配合使用才能实现全代的回收,G1将整个堆内存划分为多个等大的region(内部连续)然后每个region不同时间代表角色不固定,不过整体分为四种:Eden、Survioor、Old,
Humongous(存储大对
象)
对于G1来说整体采用的是标记-整理算法,然后从region的角度来看,采用的是复制算法

9、Jvm堆分类

jvm堆一般分为三部分: 新生代,老年代,永久代(元空间)

永久代java8已经被元空间取代。

  1. 新生代

用来存放新生的对象,占据堆的1/3空间

如果新创建的对象占用内存很大,则直接分配到老年代。(当老年代也满了装不下的时候,就会抛出OOM异常。)

  1. 老年代

老年代的对象比较稳定,所以MajorGC不会频繁执行。

在进行MajorGC前一般都先进行了一次MinorGC, 使得有新生代的对象晋身入老年代,导致空间不够用时才触发。 当无法找到足够大的连续空间分配给新创建的较大对象时 也会提前触发一次MajorGC进行垃圾回收腾出空间。

  1. 永久代(元空间)

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。

Class在被加载的时候被放入永久区域。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中。

永久代(元空间)和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。

元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

  1. 对象优先在堆的 Eden 区分配。

  2. 大对象直接进入老年代.

  3. 长期存活的对象将直接进入老年代.

当 Eden 区没有足够的空间进行分配时,虚拟机会执行一次 Minor GC.Minor Gc 通常发生在新生代的 Eden 区,在这个区的对象生存期短,往往发生 Gc 的频率较高,回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代 GC的时候不会触发 Minor GC,但是通过配置,可以在Full GC 之前进行一次 Minor GC 这样可以加快老年代的回收速度。

10、执行引擎概述

执行引擎是JVM核心组成部分之一。这里的执行引擎特制虚拟机(VM)中的执行引擎,其实物理机中也存在执行引擎。物理机中执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的,而虚拟机中的执行引擎是由软件自行实现的。因此,可以不受物理条件制约地定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式。那么,什么是不被硬件直接支持的指令集格式呢,别急,我们把这个问题放一放,后面自有解答。

工作流程

为什么需要执行引擎呢?我们知道,jvm主要任务是装载字节码到其内部,但字节码不能直接运行在操作系统上,因为它内部仅仅包含一些能被jvm所识别的字节码指令、符号表以及其他辅助信息。所以,想让java程序执行起来,需要执行引擎将字节码指令解释/编译为对应平台上的本机机器指令。
执行引擎的执行过程:
执行引擎执行过程中执行什么指令依赖于程序计数器(PC寄存器)中的地址,它会根据该地址找到对应的指令。执行完成一条指令操作后,程序计数器会更新下一条需要被执行的指令地址。
在方法执行过程中,执行引擎也可能根据虚拟机栈中局部变量表中存储的对象引用找到堆中对应的对象实例信息,还可能通过对象头中的元数据指针定位到目标对象的类型信息。

11、JVM编译

JVM执行代码主要有两种方式:
解释执行:
一段代码,解释一行执行一行。即源程序被编译为字节码文件后,解释器逐行将字节码解释成机器指令并执行。
编译执行:
事先已经被编译成机器码,直接执行,不用解释。源程序被编译为字节码文件后,JIT编译器将字节码编译为机器指令,再由cpu执行。

虽然IT编译器能提高执行效率,但是在执行机器指令前,它需要对字节码进行编译,因此造成响应时间较长,而解释器可以直接对字节码进行解释执行。因此,Hotspot虚拟机采用二者并存的方式,在jvm执行过程中,二者相互协作,各自取长补短,尽全力去权衡编译本地代码的时间和直接解释执行代码的时间
保证执行效率。
在ivm启动时,解释器可以先发挥作用,而不必等待JIT编译器全部编译完成后再去执行,省去不必要的编译的时间。而随着程序运行时间的推移,即时编译器逐渐发挥作用,根据热点探测功能,将有价值的字节码编译为本地机器指令,以换取更高的执行效率。

12、Java 异常体系

Java的异常都是派生于Throwable类的一个实例,所有的异常都是由Throwable继承而来的。Throwable有分为了Error类和Exception类。
Error(错误)
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。
Error表示比较严重的问题,一般是JVM运行时出现了错误,如没有内存可分配抛出OOM错误、栈资源耗尽抛出StackOverflowError错误、Java虚拟机运行错误Virtual MachineError、类定义错误NoClassDefFoundError。
Exception(异常)
异常又分为RuntimeException和其他异常:
由程序错误导致的异常属于RuntimeException,而程序本身没有问题,但由于像V/O错误这类问题导致的异常属于其他异常。
运行时异常RuntimeException:
顾名思义,运行时才可能抛出的异常,编译器不会处理此类异常。比如数组索引越界、使用的对象为空、强制类型转换错误、除0等等。出现了运行时异常,一般是程序的逻辑有问题,是程序自身的问题而非外部因素。
其他异常:
Exception中除了运行时异常之外的,都属于其他异常。也可以称之为编译时异常,这部分异常编译器要求必须处置。这部分异常常常是因为外部运行环境导致,因为程序可能运行在各种环境中,如打开一个不存在的文件,此时抛出FileNotFoundException。编译器要求Java程序必须捕获或声明所有的编译时异常,强制要求程序为可能出现的异常做准备工作。
不要被运行时异常的名称所迷惑,理论上所有的错误都是运行时发生的。包括Error、RuntimeException、编译时异常等等。所有的这些都只能在程序运行的过程中才能碰到。编译时异常指的是编译器要求必须处理的异常,并不是代码编译间发生的错误。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值