总结
在清楚了各个大厂的面试重点之后,就能很好的提高你刷题以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。
static {
System.out.println(“ClassLoaderTest的静态代码块66666”);
}
}
class code{
{
System.out.println(“code的代码块11111”);
}
public code(){
System.out.println(“code的构造方法22222”);
}
static {
System.out.println(“code的静态代码块3333”);
}
}
六、栈
在介绍JVM栈之前,我先了解一下 栈帧 概念
栈帧:一个栈帧随着一个方法的调用开始而创建,这个方法调用完成而销毁。栈帧内存放者方法中的局部变量,操作数栈等数据。
Java栈也称作虚拟机栈(Java Vitual Machine Stack),JVM栈只对栈帧进行存储,压栈和出栈操作。Java栈是Java方法执行的内存模型。下面我们来看一个Java栈图。
总结
-
每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象)。对象都存放在堆区中。
-
每个战中的数据(基础数据类型和对象引用)都是私有的,其他栈不能访问。
-
栈分为3个部分:基本类型变量,执行环境上下文,操作指令区(存放操作指令).
-
在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。
-
当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量
-
分配的内存空间,该内存空间可以立即被另作他用。
StackOverFlowError
java.lang.StackOverFlowError,当我们的方法过多占满了栈空间,那么就会产生该异常(本质是个错误)!
七、栈、堆、方法区之间的交互关系
HotSpot 是通过指针访问对象的!(HotSpot是我们oracle发布的JDK统称)
栈:保存着实例对象在堆内存中的地址
堆:保存着类元数据的地址
Java8中的内存分布 :
与1.7的区别:
1、字符串常量池由方法区移动到堆中
2、方法区从堆内存移出,到本地内存当中
八、堆【重点】
一个JVM实例中是只包含一个堆内存,堆内存的大小是可调节的,类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。
区别:
-
JDK 7逻辑上分为:新生区 + 养老区 + 永久区 ,实际上:新生区 + 养老区
-
JDK 8逻辑上分为:新生区 + 养老区 + 元空间 ,实际上:新生区 + 养老区
由于我们的方法区在1.7被称为永久区,在1.8被称为元空间,逻辑上是将方法区归入堆内存中!也会被称为非堆,目的就是为了和堆区分开来。
永久区占用的是堆内存中空间,元空间占用的是物理机的内存
OOM
出现oom:OurOfMemoryError : java heap space 的原因
-
JVM的堆内存不够,可以通过手动设置参数来-Xms,-Xm来调整。
-
代码中创建了大量的对象,并且长时间不能被垃圾GC回收(存在引用)。
堆参数入门
注意:我们设置的堆参数-Xmx,-xms只会作用在堆中的新生区和养老区。
| 说明 | 备注 | |
| — | — | — |
| -Xms | 初始堆的分配大小,默认为物理内存的六十四分之一 | |
| -Xmx | 堆的最大分配大小(默认为物理内存的四分之一) | |
| -Xmn | 新生代的大小 | |
测试我们JVM所占用的内存!
@SuppressWarnings(“all”)
public class HeapMemeoryTest {
public static void main(String[] args) {
long totalMem = Runtime.getRuntime().totalMemory(); //返回当前JVM占用的内存
long maxMemory = Runtime.getRuntime().maxMemory(); //返回当前JVM可占用的最大内存
System.out.println(“当前JVM占用的内存” + totalMem/1024/1024);
System.out.println(“前JVM可占用的最大内存” + maxMemory/1024/1024);
}
}
//当前JVM占用的内存245
//前JVM可占用的最大内存3625
为什么要使用2个survivor区 ? 可以不用吗 ?
参考文章 :https://blog.csdn.net/antony9118/article/details/51425581
调节堆的占用内存 :
例如 -Xms1024m -Xmx1024m -XX:+PrintGCDetails
如此将堆内存的最大和最小内存都设置为1024MB , 并打印GC日志!
堆中存放的数据
java堆是JVM内存中最大的一块,由所有线程共享,是由垃圾收集器管理的内存区域,主要存放对象实例
1、对象实例
-
类初始化生成的对象
-
基本数据类型的数组也是对象实例
2、字符串常量池
-
字符串常量池原本存放于方法区,从jdk7开始就放置于堆中。
-
字符串常量池存储的是string对象的直接引用,而不是直接存放的对象,是一张string table
3、静态变量
- 静态变量是有static修饰的变量,jdk7时从方法区迁移至堆中
4、线程分配缓冲区( Thread Local Allocation Buffer)
-
线程私有,但是不影响java堆的共性
-
增加线程分配缓冲区是为了提升对象分配时的效率
-
ava堆既可以是固定大小的,也可以是可扩展的(通过参数-Xmx和-Xms设定),如果堆无法扩展或者无法分配内存时也会报OOM
九、GC
Garbage Collection 垃圾回收机制
GC = 新生区的GC(轻量级GC,也称minor GC) +养老区fullGC(重量级GC ,majorGC)
常见面试题 *
1、什么是GC ?
答:GC垃圾回收机制,遵循分代收集的算法思路
-
频繁收集新生代
-
很少收集老年代
-
基本不动元空间(永久代)
2、在JVM当中如何判断一个对象是不是垃圾,对于垃圾啊是不是立即回收?
- 引用计数法(JVM已经弃用,Python等一些还在用) : 效率高,空应用就回收,但是对象互相引用特殊情况无法回收!
public class GCTest {
public static void main(String[] args) {
Student student1 = new Student();
Student student2 = new Student();
student1.student = student2 ; //这种情况GC就无法回收
student2.student = student1 ;
}
}
class Student{
int id ;
Student student ;
}
- GCRoots 根可达算法
所谓GC Roots 或者说tracing GC的根集合就是一组必须活跃的引用!
人话: 从Root根对象开始找,能找到的就是活跃的不可删除的,找不到的需要GC回收
可以作为GCRoot的对象
//1、t1可以叫虚拟机栈中引用的对象(由于强引用不可回收)
GCRootsDemo t1 = new GCRootsDemo();
//2、方法区中的类静态属性引用的对象
private static GCRootsDemo2 t2 = new GCRootsDemo2();
//3、方法区中常量引用的对象
private static finalGCRootsDemo3 t3 = new GCRootsDemo3(); 特点:都不会被回收的,可以作为根
3、说说你知道的垃圾回收算法有哪些 ?
1、复制算法
常用于新生代!
-
优点:不会产生内存碎片,
-
缺点:但是浪费空间,因为始终会存在一个空的幸存区 (浪费是值得的!hh)
2、标记清除算法
常用于老年代 !
第一步:标记出养老区的所有垃圾,第二步:清除所有的垃圾
-
优点:节省了空间
-
缺点:会产生内存碎片
3、标记清除压缩算法
也是常用于老年代!(慢工出细活)
在标记清除的步骤后,在将老年代通过整理算法整理一下空间,解决内存碎片问题
-
优点:解决上述的问题
-
唯一缺点:耗时
算法之间的比较
-
内存效率:复制算法 > 标记清除算法 > 标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。
-
内存整齐度:复制算法 = 标记整理算法 > 标记清除算法。
-
内存利用率:标记整理算法 = 标记清除算法 > 复制算法。
可以看出,效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存,而为了尽量兼顾上面所提到的三个指标,标记/整理算
相对来说更平滑一些,但效率上依然不尽如人意,它比复制算法多了一个标记的阶段,又比标记/清除多了一个整理内存的过程
4、JVM内存模型以及分区,需要详细到每个区放什么 ?
-
方法区:主要是存储类信息,常量池(static 常量和 static 变量),编译后的代码(字节码)等数据
-
堆:初始化的对象,成员变量 (那种非 static 的变量),所有的对象实例和数组都要在堆上分配
-
本地方法栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上面存储局部变量表,操作数栈,方法出口等信息,局部变量表存放的是 8 大基础类型加上一个应用类型,所以还是一个指向地址的指针
-
JVM方法栈:主要为 Native 方法服务
-
PC计数器:记录当前线程执行的行号
5、堆里面的分区: Eden,survival from to,老年代,各自的特点 ?
新生代:
-
Eden: Eden区位于Java堆的新生代,是新对象分配内存的地方
-
survicor:在堆内存当中有两个这种分区一种是form,一种是to,from:也是Survivor0区,to:也是Survivor1区,这两个区是相对的,在发生一次Minor GC后,from区就会和to区互换。在发生Minor GC时,Eden区和Survivalfrom区会把一些仍然存活的对象复制进Survival to区,并清除内存。Survival to区会把一些存活得足够旧的对象移至年老代。
老年代:
- 年老代里存放的都是存活时间较久的,大小较大的对象,因此年老代使用标记整理算法。当年老代容量满的时候,会触发一次Major GC(full GC),回收年老代和年轻代中不再被使用的对象资源。
最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
,并清除内存。Survival to区会把一些存活得足够旧的对象移至年老代。
老年代:
- 年老代里存放的都是存活时间较久的,大小较大的对象,因此年老代使用标记整理算法。当年老代容量满的时候,会触发一次Major GC(full GC),回收年老代和年轻代中不再被使用的对象资源。
最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
[外链图片转存中…(img-xKtMDGa4-1714922595549)]