Java内存模型 gc算法_JVM、java内存模型复习笔记

java虚拟机由4个部分组成,Class Loader类加载器,加载class文件到内存中

Execution Engine解析class文件的字节码指令去执行

Natvie Inerface 融合不同开发语言的原生库为java所用

Runtime Date Area JVM内存空间结构模型

为什么使用反射?

java反射机制是在运行状态中,对于任意一个类,都是够知道这个类的属性和方法,对于任意一个类都能够调用它任意的方法和属性,这种动态获取信息的以及动态调用对象方法的功能称之为java反射。

类从编译到执行的过程

编译器将Robot.java源文件编译成Robot.class字节码文件

ClassLoader将字节码文件转换成Class对象

什么是ClassLoder?

ClassLoder在java中有这非常重要的作用,它主要工作在class装载的加载阶段,其主要作用是从系统外部获取Class二进制数据流,它是java的核心组件。所有Class都是由ClassLoader进行加载的,ClassLoader复制将Class文件里的二进制数据流装载进系统然后交给Java虚拟机进行连接,初始化等操作。

ClasLoder的种类BootStrapClassLoder :C++编写 ,加载核心类库 java.*

ExtClassLoader: java编写加载扩展库 javax.*

AppClassLoader : java编写,加载程序所在目录

自定义ClassLoader: java编写定制化加载

ClassLoader的双亲委派机制

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

类的加载方式隐式加载:new

显式加载:loadClass,forName,

Class.forName得到的class是已经初始化完成的(也就是初始化了类静态变量的值以及代码块代码)

ClassLoader.loadClass得到的是class还没有链接的(只是加载到内存中)

java的内存模型

线程私有: 程序计数器、虚拟机栈,本地方法栈。

线程共享的:mateSpace、java堆程序计数器 1当前线程所执行的字节码行号指示器,2改变计数器的值来选取下一条需要执行的字节码指令,3和线程是一对一的关系即“线程私有”,4java方法计数如果是native方法则计数器值为undefined,5不会发生内存泄漏。

java虚拟机栈 1java方法执行的内存模型,2包含多个栈帧。

本地方法栈 这部分主要与虚拟机用到的 Native 方法相关,一般情况下, Java 应用程序员并不需要关心这部分的内容。

java堆是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过 GC 进行回收。

mateSpace 元空间。

JVM三大性能调优参数-Xms、-Xmx、-Xss的含义

-Xss:规定了每个线程虚拟机栈(堆栈)的大小。258k足够,影响并发线程数的大小。

-Xms:初始的java堆的大小,改线程刚创建出来的java堆的大小。如果超过到了java堆容量就会扩容,扩容至-Xmx大小。

-Xmx:java堆能扩容至最大大小。(一般Xms和Xmx设置一样,应该扩容时发生内存抖动影响性能)

java内存的堆和栈的区别

-内存分配策略静态存储;在编译时确定每个数据目标在运行时的存储空间需求。

栈式存储:动态存储,数据区需求在编译时未知,运行时模块入口前确定。

堆式存储;编译时或运行时模块入口都无法确定,动态分配。

管理方式;栈自动释放,堆需要GC处理。

空间大小;栈比堆小,

碎片相关;栈产生的碎片远小于堆。

分配方式;栈支持静态分配和动态分配,而堆仅仅支持动态分配。

效率;栈的效率比堆高

不同JDK版本之间的intern()的区别---JDK6 VS JDK7+

String s = new String("a");

s.intern();jdk6 ; 当调用intern方法时,如果字符串常量池先前已创建出改字符串对象。则返回池中该字符串的引用,否则将此字符串对象添加到字符常量池中,并且返回改字符串对象的引用。

jdk7+; 当调用intern方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中改字符串的引用,否则将该字符串对象已经已经在与java堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。

GC垃圾回收机制

什么对象被判定为垃圾的标准?

当一个对象没有被任何对象引用时,对系统而言它就是垃圾,占据的内存就应该被释放,同时此对象应该被销毁。

判断对象有没有被引用有两种方法:引用计数算法,通过判断对象的引用数量来决定对象是否可以被回收,每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1。优点:直接判断引用计数器为0的对象,执行效率高,程序执行受影响较小。确定是无法检测出循环引用的情况,导致内存泄漏。

可达性分析算法,通过判断对象的引用链是否可达,离散数学图论,程序把所有的引用关系看做一张图,从GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路劲,就被成为引用链,Reference Chain,当一个对象从GC Roots没有Reference Chain和引用链相连,从图论上来说就是从GC Roots到这个对象是不可达。这样就被认为这个对象是不可用,这样这个对象就被标记为垃圾了。

可用做为GC Root的对象,a.虚拟机栈中引用的对象(栈帧中的本地变量表)。b.方法区中的常量引用的对象。 c.方法区中的类静态属性引用的对象。d.本地方法栈中JNI(Native方法)的引用对象。e.活跃线程的引用对象。

谈谈你了解的垃圾回收算法

标记清除算法;标记:从根集合进行扫描对存活的对象进行标记。

清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存。

标记清楚算法由于不需要移动原本的对象位置,直接抽取清除垃圾对象,比较高效,但是内存碎片化比较严重。

复制算法;

分为对象面和空闲面,对象在对象面上创建,存活的对象被从对象面复制到空闲面,将对象面所有的对象内存清除。

标记整理算法;标记:从根集合进行扫描对存活的对象进行标记。

清除:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。

分代收集算法;

垃圾回收算法的组合拳,按照对象生命周期的不同划分以采用不同的垃圾回收算法,目的提高JVM的回收效率。(java6,7堆内存分为年轻代、老年代和永久代这三个模块,jdk8版本永久代被去除,年轻代对象存活率低采用复制算法,老年代对象存活率高采用标记清楚算法或者标记整理算法)

分代收集算法的GC分为两种

Minor GC ;是在年轻代中工作,采用的是复式算法...

常见的垃圾收集器

年轻代常见的垃圾收集器

Serial收集器(-XX:+UseSerialGC,复制算法)单线程收集,进行垃圾收集时,必须暂停所有的工作线程

简单高效,Client模式下默认的年轻代收集器

ParNew收集器(-XX:+UseParNewGC,复制算法)多线程收集,其余的行为,特点和Serial收集一样

单核执行效率不如Serial,在多核执行才有优势

Pa(Ru):包含Running等待线程调度选中和Ready在线程池中等待线程调度和选中比起关注用户线程停顿时间,更关注系统的吞吐量。(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))

在多核执行才有优势,Server模式下默认年轻代收集器

老年代常见的垃圾收集器

Serial Old收集器(-XX:+UseSerialOldGC,复制算法)单线程收集,进行垃圾收集时,必须暂停所有工作线程

简单高效,Client模式下默认的老年代收集器

Parallel Old收集器(-XX:+UseParallelOldGC,标记-整理算法)多线程,吞吐量优先CMS收集器(-XX:+UseConcMarkSweepGC,标记-清除算法)

G1收集器(-XX:+UseG1GC,复制+标记-整理算法)

Garbage First 收集器的特点并发和并行

分带收集

空间整合

可预测的停顿

将整个Java堆内存划分多个相等的region

年轻代和老年代不在物理隔离

GC相关的面试题QC

Object的finalize()方法的作用是否与C++的析函数作用相同?与C++的析构函数不同,析构函数调用确定,而它的是不确定的

将未被引用的对象放置于F-Queue队列

方法执行随时可能被终止

给予对象最后一次重生的机会

Java中的强引用,软引用,弱引用,虚引用有什么用。

强引用(Strong Reference)最普遍的引用:Object obj = new Object()

抛出OutOfMemoryError终止程序也不回被回收具有强引用的对象

通过将对象设置为null来弱化引用,使其被回收

软引用(Soft Reference)对象处在有用但是非必须状态

只有当内存空间不足时,GC会回收改引用的对象的内存

可以用来实现内存敏感的高速缓存

String str = new String("abc");

SoftReference softRef = new SoftReference(str);

弱引用(Weak Reference)GC时被回收

被回收的概率也不大,因为GC线程优先级比较低

适用于引用偶尔被使用且不影响垃圾收集的对象

String str = new String("abc");

WeakReference softRef = new WeakReference(str);

虚引用(PhantomReference)不会觉得对象的生命周期

任何时候都可能被垃圾回收器回收

跟踪对象被垃圾回收器回收的活动,起哨兵作用

必须和引用队列ReferenceQueue联合使用

String str = new String("abc");

ReferenceQueue queue = new ReferenceQueue(str);

PhantomReference ref = new PhantomReference(str ,queue)

引用队列(ReferenceQueue)无实际存储结构;存储逻辑依赖于内部节点之间的关系来表达

存储关联的且被GC的软引用,弱引用以及虚引用

关于JDK版本的选择

最好选择JDK8,JDK11,因为这是Oracle长期支持的版本。

什么是进程和什么是线程

进程和线程的由来串行:初期的计算机智能串行执行任务,并且需要长时间等待用户的输入

批处理:预先将用户的指令集中成清单,批量串行处理用户的指令,仍然无法并发执行

进程:进程独占内存空间,保存各自运行的状态,互相间不干扰且可以相互切换,为并发处理任务提供了可能。

线程:共享进程的内存资源,相互间切换更加快速,支持更细粒度的任务控制,使进程内的子任务得以并发执行。线程不能看做独立应用,而进程可看做独立的应用

进程有独立的地址空间,互相不影响,线程只是进程的不同执行路径

线程没有独立的地址空间,多进程的程序比多线程的程序健壮

进程的切换比线程的切换开销大

Java进程和线程的关系?Java对操作系统提供你的功能进行封装,包括进程的线程

运行一个程序会产生一个进程,进程包装至少一个线程

每个进程对应一个JVM实例,多个线程共享JVM里的堆

Java采用单线程编程模型,程序会自动创建主线程

主线程可以创建子线程,原则上要后于子线程完成执行

Thread中的start和run方法的区别?调用start()方法会创建一个新的子线程并启动

run()方法是是Thread的一个普通方法的调用

Thread和Runnable是什么关系?Thread是实现了Runnable的类,使得run支持多线程

因类的单一继承原则,推荐多使用Runnable接口

如何给run()方法传参?构造函数传参

成员变量传参

回调函数传参

如何实现处理线程的返回值主线程等待法

使用Thread类的join()阻塞当前以等待子线程处理完毕

通过Callable接口实现:通过FutureTask 或者线程池获取

线程的六个状态新建(New):创建后尚未启动的线程的状态,(新创建了一个线程对象,还没调用start方法)

运行(Runneble):包含Running等待线程调度选中和Ready在线程池中等待线程调度和选中

无限期等待(Waiting):不会被分配CPU执行时间,需要显式被唤醒没有设置Timeout参数的object.wait()方法。

没有设置Timeout参数的Thread.join()。

LockSupport.part()方法。限期等待(Timed Waiting):在一定时间后会由系统自动唤醒Thread.sleep()方法。

设置了Timeout参数的object.wait()方法。

设置了Timeout参数的Thread.join()。

LockSupport.parkNanos()方法。

LockSupport.parkUntil()方法。阻塞(Blocked):等待获取排他锁

结束(Terminated):已终止线程的状态,线程已经结束执行

sleep和wait的区别

基本差别sleep是Thread类的方法,wait()是object类中定义的方法

sleep方法可以在任何地方使用,wait()方法只能在synchronize方法或者synchronized块中使用

最主要的本质区别Thread.sleep()只会让出CPU,不会导致锁行为的改变

Object.wait()不仅让出CPU,还会释放占有的同步资源的锁

notify和notifyAll的区别

两个概念锁池EntryList,当其他线程执行到有锁方法时,无法获取锁只能阻塞时,当前线程进入锁池

等待池WaitSet,当使用wait方法释放锁,进入等待池,等待其他线程唤醒或者等待期限结束。

notifyAll会让所有处于等待的线程全部进入锁池去竞争获取锁的机会,notify只会随机选取一个处于等待池的线程进入锁池去竞争获取锁的机会

--个人理解当所有线程需要锁才能执行下,notify只会随机唤醒一个线程去抢锁执行,其他线程没有被唤醒根本不会去抢锁。notifyAll会唤醒所有的线程去抢锁,抢到锁的执行,没有抢到锁的在锁池,一旦抢到锁依然会执行。

yield的概念

当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能忽视这个暗示。(就算出让CPU也不会释放锁)

如何中断线程

调用interrupt(),通知线程应该被中断了如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InerruptException异常

如果线程处于正常活动状态,那么线程将改线程的中断标识设置true,被设置中断标志的线程将继续正常运行不受影响。

需要被调用的线程配合中断在正常运行的任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程

如果线程处于正常的活动状态,那么会将该线程的中断标识设置为true,被设置中断标志的线程将继续正常运行,不受影响。

synchronized

互斥锁的特效互斥性:及在同一时间只允许一个线程持有某个对象的对象锁,通过这个特效来实现多线协调机制,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问,互斥性也称之为操作的原子性。

可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于最后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能在本地缓存的某个副本上继续操作,从而引起不一致。

根据获取锁的分类:获取对象锁和获取类锁

获取对象锁同步代码块(synchronized(this),synchronized(类的实例),锁的是小括号()中的实例对象)。

同步非静态方法,synchronized method,锁是当前对象的实例对象。

获取类锁同步代码块(synchronized(类.class)锁的是小括号()中的Class对象)。

同步非静态方法,synchronized static method,锁是当前对象的类对象,Class对象。

对象锁和类锁的总结有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块;

若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程会被阻塞;

若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象的同步方法的线程会被阻塞;

若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步方法的线程会被阻塞;反之亦然;

同一个类的不同对象的对象锁互不干扰;

类锁由于也是一直特殊的对象锁,因此和上诉1,2,3,4一致,而由于一个类只有一把对象锁,所有同一个类的不同对象使用类锁,将会是同步的;

类锁和对象锁互不干扰;

synchronized底层实现原理

这个概念性的东西太多了,还有JVM指令,容我先缓缓神...o(╥﹏╥)o

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值