深入理解java虚拟机
第二章 自动内存管理机制
- 1.运行时数据区域(jvm运行java程序时将内存分为不同的数据区域)
-
A.程序计数器—记录的是当前线程执行字节码指令的行号指示器(线程私有),字节码解释器通过改变计数器的值来选取下一条需要执行的字节码命令,执行java方法会记录字节码指令的地址,执行本地方法为null
-
B.虚拟机栈—虚拟机栈生命周期和线程相同,代表java方法执行的内存模型,每个方法调用都会产生一个栈帧,方法调用的开始到结束,对应着栈帧的入栈到出栈(每个栈帧里有局部变量表,动态链接,方法出口,操作数栈),栈的深度太深导致栈内存溢出,虚拟机栈内存申请内存不足会导致内存溢出(Xss设置虚拟机栈)
-
C.本地方法栈—和虚拟机栈差不多,只不过是调用native方法,栈的深度太深导致栈内存溢出,虚拟机栈内存申请内存不足会导致内存溢出(Xoss设置本地方法栈无效)
-
D.堆—虚拟机启动时创建(存放所有实例对象,和数组),分代收集算法主要处理的内存区域(物理上不连续,逻辑上连续也可以),通过Xmx(堆最大)和Xms(堆最小)控制(Xmn新生代内存大设置),如果队中没有内存分配实例,并且内存也无法扩展就会爆出内存溢出的异常
-
E.方法区—存储的是虚拟机加载的类的相关信息,常量和静态变量等,无法满足内存分配就会爆出内存溢出,主要是对常量池的回收(-XXPermSize 和-XXMaxPermSize 通过控制方法区内存大小可以控制常量池大小)
-
F.运行时常量池,方法区的一部分,用于存放编译期生成的各种字面量和符号引用(类和接口的全限定名,),除了编译期运行期也可以把新的常量放入池中(如String的intern方法,字段的名称和描述符,放大法的名称和描述符,虚拟机运行时需要从常量池获取对应的符号引用,在创建类或运行时解析成对诺对应的内存地址)
-
- 2.直接内存(非运行时数据区域),主要用于nio编程(基于channel和buffer),直接调用本地方法通过在堆内存产生一个directbytebuffer对象引用可以直接操作堆外内存,避免了java堆和native堆的数据复制,(解释:java堆中的数据要传输到外界,需要先复制到native堆,因为直接使用native堆(堆外内存)则不需要进行复制)通过操作系统分配堆外内存不受垃圾回收机制管理,需要手动进行回收(-XXMaxDirectMemorySize设置直接内存大小,不设置默认和堆最大内存相同)
- 3.对象访问的两种方式(Object obg = new Object())等号左边obj作为一个引用类型存放在java虚拟机栈的局部变量表中,等号右边作为一个实例对象存放在堆中。
如何通过这个引用访问这个实例对象?- A.使用句柄池(java堆内存开辟一块内存),引用中存储的就是对象的句柄地址(句柄中又同时包含了对象实例数据和类型数据的内存地址)
- B.使用直接指针的方式,引用直接指向对象的实例数据,实例数据中包含指向数据类型的引用(sun hotspot使用方式)
- 4.内存溢出分析(OOM)
- A.java对内存溢出(创建大量的实例对象,如集合中不停地添加对象)
- B.虚拟机栈和本地方法栈(栈的容量大,或栈的深度深)
- C.运行时常量池溢出(方法区内存小,运行时常量池里添加常量)
- D.方法区溢出(加载大量的class,大量的类信息填满方法区)
- E.直接内存溢出(向操作系统申请内存,通过计算得知内存不够,手动抛出异常)
==第三章 == 垃圾收集器和内存分配策略
1.内存泄露----内存无法回收
2.内存溢出----申请内存不够
1.程序计数器,虚拟机栈,本地方法栈,随线程生灭,虚拟机栈中的栈帧随方法调用开始和调用结束而入栈出栈而生灭,所以这部分的内存分配和回收都具备确定性,方法结束,线程结束,内存就跟着回收了。
2.堆和方法区,因为一个接口的多个实现类需要内存可能不一样,一个方法的不同分支可能需要的内存也不一样。只有运行期才知道会创建那些对象,这部分内存分配和回收是动态的,垃圾回收器关注这部分内存。
- 3.判断对象是否存活(是否已死的算法)
- A.引用计数算法(难以解决循环引用的问题,两个对象之间相互引用,其他再无任何引用,计数器永远不为0)给对象添加一个引用计数器,有地方引用计数器加1,引用失效计数器减1.当计数器为0时,该对象不可用。
- B.根搜索算法(可达性算法)通过一系列称为GC ROOT的对象为起始点,树形构成一个引用链向下搜索,其中不可达的对象即为要被回收的对象(引用链不可达第一次标记并进行一次筛选,判断对象是否有必要性执行finalize方法,当对象没有覆盖finalize方法或已经被虚拟机调用过,就会被认为没有必要执行。如果有必要执行finalize犯方法就会把该对象放到一个F-Queue队列中,并且稍后虚拟机会创建一个低优先级的线程Finalizer去执行,这里执行是指虚拟机会触发这个方法,但是不一定保证方法会执行完成,有可能执行过程中对象就会被回收掉了,如果执行方法后对象重新与引用链建立了关系,gc会对F-Queue队列中的对象进行第二次小规模标记,在第二次标记的时候就会把该对象移出即将回收的集合,否则很有可能就会被回收掉了),(1.方法区中常量池中引用的对象,2.方法区中类的静态变量引用的对象,3.虚拟机栈栈帧中的本地变量表中引用的对象,都可以做GC ROOT的起始节点),这样的话,对象之间的相循环引用会被回收(java采用这种方式)。
4.传统引用的定义(如果引用类型数据存储另一块内存地址的起始值,我们就称这块内存代表一个引用,对象只有被引用和没有被引用两种状态)java1.2以后有这样一类对象,在内存空间充足时保存这类对象,在gc过后内存还是有些紧张的时候回收这些对象,
- A.强引用
Object obj = new Object();
//可直接通过obj取得对应的对象 如obj.equels(new Object());
而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。 - B.软引用----SoftReference类实现软引用,还有用,非必须的对象,在系统要出现内存溢出时进行二次gc,如果内存还不够就会爆出异常,可以做系统缓存(如,大量默认头像,不用每次去路径读取,可以在一次读取后将对象信息使用软引用缓存起来)
Object obj = new Object();
SoftReference sf = new SoftReference(obj);
obj = null;
sf.get();//有时候会返回null
这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;
软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。 - C.弱引用-----WeakReference类实现弱引用,弱引用只能生存到下一次垃圾回收之前,无论内存是否足够都会被回收掉
Object obj = new Object();
WeakReference wf = new WeakReference(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。
弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。 - D.虚引用-----PhantomReference类实现弱引用,无法通过虚引用来获取一个对象实例,使用虚引用唯一的目的是对象被回收时获得一个系统通知
Object obj = new Object();
PhantomReference pf = new PhantomReference(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
虚引用主要用于检测对象是否已经从内存中删除。
5.方法区回收
A.回收废弃常量
B.回收无用的类(1所有的实例对象都会被回收,2加载这个类的ClassLoader被回收,堆中生成的Class对象没有任何地方被引用,无法通过反射访问该类的方法)只具备了回收资格,还需要虚拟机做参数设置
6.垃圾回收算法的实现。(前提是根搜索算法中已经被标记了需要被回收的对象)
- A.标记-清除算法,在之前根搜索算法中标记的对象,会被统一清除,(缺点:会导致大量的内存碎片(物理地址不连续),当需要内存给较大对象时,会因为没有连续的大内存空间导致gc频发。影响效率)
- B.复制算法,将内存分为两块,每次使用其中一快,当这块内存使用完了,就将活着的内存复制到另一块内存中,然后清理完这块内存,(缺点,内存变小了,对于老年代存活周期长的对象,经常复制效率低下, gc频发)现在商业的虚拟机都是用这种(8:1:1)的复制算法------适用于新生代)
- C.标记-整理算法,存活的对象通过指针移动到一块内存连续的区域。----适用于老年代)
- D.分代收集算法(商业虚拟机使用)根据对象存活周期不同将内存分为新生代(死多生少采用复制算法)和老年代(存活周期长,采用标记整理或标记清除算法)
1.大对象直接进入老年代,避免在新生代进行复制算法S1,S2之间发生大量拷贝,2长期存活的对象进入到老年代(虚拟机给每个对象设置一个对象年龄计数器,新生代gc存活一次年龄加1,默认是15岁的时候就会被移动到老年代)
7.垃圾收集器(回收算法是方法论,垃圾回收器就是具体实现)
A.Serial收集器----单线程收集器(他工作时候必须暂停其他所有的工作线程)直到他结束,
B.PaeNew收集器—Serial收集器的升级版本多线程收集器(他工作时候必须暂停其他所有的工作线程)直到他结束,
C.Parallel scavenger收集器
D.Serial old收集器—单线程收集器(他工作时候必须暂停其他所有的工作线程)直到他结束,
E.
F.Parallel old收集器
G.Cms 收集器
H.g1收集器
第四章虚拟机性能监控和故障处理
1.jdk可视化工具
A.JConsole连接不同的进程---------监视和管理控制台
B.JVisualVM多合一故障处理工具
第五章虚拟机性能调优和案例分析
第三部分虚拟机执行子系统
第四部分程序编译和代码优化
第五部分高效并发
本文深入探讨Java虚拟机(JVM)的自动内存管理机制,包括运行时数据区域的划分及其作用,如程序计数器、虚拟机栈、本地方法栈、堆、方法区和运行时常量池。同时,介绍了直接内存的概念及对象访问方式,详细分析了各种内存溢出情况,以及垃圾收集器和内存分配策略。
410

被折叠的 条评论
为什么被折叠?



