JVM
1.垃圾回收不涉及栈内存,因为栈帧进栈后,方法调用完后,会弹出栈,直到主方法也弹出栈,最后栈就成空的了,所以不用垃圾回收来管理栈内存,垃圾回收主要是回收堆内存中的无用对象;
2.栈的内存并不是越大越好,栈内存越大,意味着线程就越少,因为物理内存大小是一定的,可以通过启动参数-Xss (size)来指定大小;
3.方法内的局部变量的安全,要看局部变量是普通局部变量,还是共享的变量,因为每个线程都有属于自己的栈,每个线程执行时,普通变量都会初始化然后进入栈;但是如果变量前加了static修饰,变成了共享的变量,就可能出现线程安全问题;
4.如果方法内的局部变量没有逃离方法的作用范围,它就是线程安全的;如果局部变量引用了对象,并逃离了方法的作用方法,需要考虑线程安全问题;
5.栈内存溢出(StackOverFlow):
-1.栈帧过多,导致的内存溢出:例如,使用了递归方法但并没有设置好出口,就会导致栈帧过多;
-2.栈帧过大;
6.JSON对象转换;
-1.Jackson:
// 依赖
// <dependency>
// <groupId>com.fasterxml.jackson.core</groupId>
// <artifactId>jackson-databind</artifactId>
// </dependency>
ObjectMapper mapper = new ObjectMapper();
// 对象转JSON字符串
String jsonStr = mapper.writeValueAsString(user);
// JSON字符串转对象
User user = mapper.readValue(jsonStr, User.class);
-2.Fastjson:
// 依赖
// <dependency>
// <groupId>com.alibaba</groupId>
// <artifactId>fastjson</artifactId>
// </dependency>
// 对象转JSON
String jsonStr = JSON.toJSONString(user);
// JSON转对象
User user = JSON.parseObject(jsonStr, User.class);
7.堆内存溢出(OutOfMemory):
-1.堆内存可以通过-Xmx(size)参数来控制;
-2.终端可视化查看指令:jconsole
8.方法区:
-1. jdk1.8以前永久代内存溢出(PermGen space)
-2. jdk1.8以后元空间内存溢出(Metaspace)
-3.常量池:常量池就是一张常量表,存储了字面量,符号引用,运行时常量, 虚拟机指令根据这张表找到要执行的类名,方法名,参数类型,字面量等信息;
-4.运行时常量池:常量池时.class文件中的,当该类被加载,它的常量池信息就会被放入运行时常量池,并把里面的符号地址变为真实地址;
9.stringTable(串池):
-1.hashtablea结构不能扩容
-2.每创建一个字符串就会往串池里找,如果找到了,就使用串池中的对象,没找到就创建一个字符串对象,并添加到串池中
-3.如果字符串是两个字符串对象拼接的,就会new出一个新的字符串对象,而不是去串池中找;如果是两个字符串拼接的就会去串池中找;
例如:string s1="ab";
string s2="a"+"b";
string s3="a";
string s4="b";
string s5=s3+s4; -->相当于new stringbuilder().append("a").append("b").build();
s1==s2 ->true
s1==s5 ->false
-4.intern()方法,可以主动将串池中没有的字符串对象放入串池;
str.intern()会先去串池中找有没有相应的字符串,有的话就返回串池中的对象,没有的话会把字符串放入串池,并返回相应对象;
-5.垃圾回收
-6.性能调优:
--1.通过-XX:StringTableSize=(size)参数来设置桶的大小来提高效率;
--2.通过intern()方法来将字符串放进串池,来忽略重复的字符串来提高效率;
-7.哪些字符串会入池,哪些不会:(ai询问答案,此处略)
10.直接内存: 不受JVM内存回收管理,需要手使用unsafe对象主动使用freememory才能回收内存;
11.垃圾回收:
-1.判断垃圾(如何判断一个垃圾是否要回收):
--1.引用计数法;
--2.可达性分析算法(java虚拟机使用的):从一组 GC Roots(根) 出发,沿引用链向下搜索,凡是能被 GC Roots 直接或间接引用到的对象都是存活的,否则是垃圾;
类似于一串葡萄,拿起来能被根连着的不掉下来的就是有用的,没连着的掉下来的就是垃圾,要被回收
-1.哪些对象可以作为GC Root对象
--1. 虚拟机栈中引用的对象
局部变量、方法参数等:
--2. 本地方法栈(JNI)中引用的对象
JNI 调用的 native 方法中持有的引用。
--3. 方法区中类的静态变量
static Object obj = new Object();
obj 是 GC Root
--4. 方法区中常量引用的对象
例如字符串常量池中的字符串被引用。
--5. ClassLoader 持有的对象
--6. 运行时的线程对象
--7. JVM 内部引用的对象
如系统类加载器、线程对象等。
-2.五种引用:
--1.强引用:被引用的对象不会被垃圾回收机制回收掉
--2.软引用:没有强引用的条件下,进行垃圾回收,且内存不足就会被回收掉; 可以配合引用队列来释放软引用自身
--3.弱引用:没有强引用的条件下,进行垃圾回收,不管内存是否不足都会被回收掉; 可以配合引用队列来释放弱引用自身
--4.虚引用:虚引用是 java.lang.ref.PhantomReference<T>,它是最“弱”的引用类型。它不会通过 get() 返回所引用的对象(get() 永远返回 null); 必须配合引用队列使用;
没有强引用后,引用的对象会被垃圾回收机制回收掉,同时进入引用队列,再将与之关联的直接内存回收
--5.终结器引用:类似于监控对象的finallize()方法;使用了就回收相应垃圾
-3.回收算法:
--1.标记清除:效率高,但是容易内存碎片化;
--2.标记整理:不会出现内存碎片化,但效率较低
--3.复制:不会出现内存碎片化,但是会占用双倍内存空间
-4.分代回收:分代回收就是根据对象的“生命周期长短”把堆划分为年轻代和老年代,并分别采用最适合的回收算法(复制 + 标记整理)来提升 GC 性能;
--1.新生代分为伊甸园,幸存区from,幸存区to; 对象首先会分配到伊甸园区域,当伊甸园内存不足时,会发生minor Gc按复制的垃圾回收算法进行垃圾回收,伊甸园和幸存区from中存活的对象copy复制到幸存区to中,存活的对象年龄加一;并且幸存区from和幸存区to交换,保证幸存区to是空的;
当对象寿命到达一定阈值(最大是15,因为对象头中存储寿命的部分是4bit, 4个二进制最大为15)时就会转移到老年代中;
--2.minor Gc会导致stop the world,暂停其他用户的线程,等垃圾回收结束,用户线程才会恢复运行;
--3.当老年代空间不足,会先尝试minor Gc,如果空间仍然不足,就会执行full Gc;
-5.Gc分析:启动时再编辑运行配置中添加虚拟机参数-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc;要先在编辑配置中,在修改选项里勾选添加虚拟机选项;
-6.垃圾回收器:
--1.串行(Serial): -XX:+UseSerialGC=serial+serialOld
--2.吞吐量优先(Parallel)(并行执行,用户线程能同时运行,但是垃圾回收时还是会STW(stop the world)): -XX:+UseParallelGC//指定新生代使用parallel并行收集器执行内存回收任务;-XX:+UseparallelOldGC//指定老年代使用并行回收收集器
--3.响应时间优先(CMS)(并行执行,用户线程能同时运行,但是部分垃圾回收阶段还是会STW)CMS 是一种以最小停顿为目标的老年代并发回收器,使用标记-清除算法,但会产生碎片,因此被 G1 替代: -XX:+UseConcMarkSweepGC
CMS分为四个阶段:初始标记阶段(会STW),并发标记阶段,重新标记阶段(会STW),并发清除阶段; <具体每个阶段所作的事,ai搜索学习>
-7.G1: G1 是 HotSpot JVM 中面向服务端/大内存/低延迟应用的垃圾回收器;G1 不再像 CMS 一样分代 + 连续内存,而是把整个堆拆成多个大小相同的 Region;每个 Region 会动态被当成:Eden,Survivor,Old,Humongous(巨型对象区 > 一半 Region);w
--1.young GC: 回收 Eden → Survivor,使用 STW + 并行复制
--2.Mixed GC: 当老年代逐渐增大时,G1会在 Young GC 的基础上额外选取 部分 Old Region(垃圾最多的) 进行回收;
--3.Full GC: 极端情况(比如 RSet 太大、晋升失败等),会触发 单线程 Full GC(很慢)
--4.G1 的最大价值是可预测停顿。可以设置:-XX:MaxGCPauseMillis=200; G1 会智能选择:在 200ms 内能回收最多垃圾的Region 集合。如果超过可预测范围 → 调整算法或进入 Full GC。
--5.young GC跨代引用 <ai搜索学习> :使用写屏障记录“老年代对象引用年轻代”的情况,通过卡表 + RSet,让 Young GC 只扫描少量卡片,不必遍历整个老年代;
--6.写屏障:用来记录引用关系的变化,以支持并发 GC 或跨代引用处理;GC 过程中,对象之间的引用会不断变化,写屏障负责把这些变化记录下来,帮助 GC 稳定、正确地工作。
-8.GC调优:
--1.java终端运行java -XX:+PrintFlagsFinal -version | findstr "GC"可以查看GC相关信息
--2.新生代内存不足,执行的是minorGC(标记复制算法),会STW,可以适当使用-Xmn虚拟机参数来加大新生代内存,但是并不是新生代内存越多就越好,新生代内存多了,相对老年代内存就少了,而老年代内存不足执行的是FullGC,STW时间会更久;所以一般控制新生代内存占堆的25%~50%;
--3.新生代晋升到老年代的阈值会根据幸存区内存大小自动调节,如果幸存区内存太小,阈值就会相对较小,容易使得其实应该最终变为垃圾的对象进入老年代,导致占用老年代的内存,只有等到fullGC时才能被回收掉,延长了垃圾存活的时间,这是不好的;所以要尽量使得幸存区内存够大,大到能保留当前活跃对象和需要晋升的对象
--4.但是又想要长期存活的对象尽快进入老年代,来减少幸存区中from到to的对象,因此又要设置较小的晋升阈值;因此设置一个合理的晋升阈值很重要;-XX:MaxTenuringThreshold=(Threshold)//设置最大晋升阈值 -XX:+PrintTenuringDistribution//打印详细页
--5.老年代的内存也是越大越好,可以先不调试,记录fullGC时内存的大小,在此基础上适当调大1/4~1/3;
-9.GC调优案例:
--1.minorGC和fullGC频繁: 通过检测工具发现新生代内存太小了,幸存区太小,对象晋升阈值小,增大新生代内存,增大幸存区空间,增大幸存区中对象晋升阈值,减少fullGC出现
--2.(CMS)请求高峰期发生了fullGC,STW时间很长: 查看GC日志,发现在重新标记阶段花费了很多时间,重新标记阶段会扫描整个堆的内存,在请求高峰期,对象较多,耗费时间较长; 使用参数-XX:+CMSScavengeBeforeRemark,在重新标记之前垃圾清理
12.类加载和字节码技术:
-1.类文件结构 <ai查询>
-2.字节码指令
--1.图解运行流程: 任务:深刻理解 a++ + ++a + a--;
--2.条件判断指令
--3.循环控制指令
--4.多态原理
--5.异常
--6.锁
-3.语法糖:
--1.默认构造器
--2.自动拆装箱
--3.泛型集合取值
-4.类加载阶段
--1.加载
--2.链接
--3.初始化
-5.类加载器
-6.运行期优化
--1.即时编译
13.JMM(java memory model) java内存模型
--1.可见性
--2.有序性
--3.CAS和原子类
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Hexo!