1.JVM内存结构
1. 方法区(Method Area)
方法区是JVM内存结构的一部分,用于存放类的相关信息,包括:
- 类的结构(字段、方法、常量池等)。
- 字段和方法的描述,如名称、类型、访问修饰符等。
- 静态变量。
- Java虚拟机在运行时加载的类的信息。
方法区通常在堆区的外部,与堆区是分开的。因为主要存放的是类的信息和静态数据,所以这一部分数据的生命周期与类本身相同。
2. 堆(Heap)
堆是JVM中用于动态分配内存的区域,是对象和数组的存储区。堆内存是JVM运行过程中最大的内存区域,所有对象实例和数组都是在这个区域中分配的。堆区又可以分为几个部分:
- 新生代(Young Generation):用于存放新创建的对象,年轻代又可以分为三个部分:Eden空间和两个Survivor空间。新创建的对象首先在Eden区分配,经过垃圾回收后存活下来的对象会被移动到Survivor区。
- 老年代(Old Generation):用于存放长期存活的对象。经过多次垃圾回收,仍然存活的对象会被转移到老年代。
- 持久代(PermGen)/元空间(Metaspace):在旧版本的JVM中,方法区使用持久代来存储类元信息。在Java 8及其之后的版本中,持久代被元空间替代,主要用于存储类的元数据,不再属于Java虚拟机的堆内存,而是使用本地内存。
3. 虚拟机栈(VM Stack)
虚拟机栈用于存储局部变量、操作数栈、动态链接等信息。每个线程都有自己的虚拟机栈。栈是先进后出(LIFO)的结构,栈中的每一个栈帧(Stack Frame)对应一个方法的调用。在方法调用时,相关的局部变量、参数值以及方法返回地址等都会被推入栈中,而在方法返回时,栈帧会被弹出。
4. 本地方法栈(Native Method Stack)
本地方法栈用于支持JVM调用本地(Native)方法,类似于虚拟机栈。它存储的是本地方法的栈帧。不同的JVM实现可能会有所不同,但其功能主要是协助执行 Java 与其他语言(如C、C++)交互的本地方法。
5. 程序计数器(Program Counter Register)
程序计数器是一个较小的内存区域,保存着当前线程所执行的字节码的地址。它是线程私有的,每个线程都有一个程序计数器,以便在多线程环境中跟踪每个线程的执行点。可以理解为一个指针,指向当前正在执行的指令。
2.垃圾回收机制(GC)
垃圾回收算法
垃圾回收算法有多种,常见的有:
-
标记-清除(Mark-and-Sweep):
- 该算法分为两个阶段。第一阶段“标记”会遍历所有可达对象并标记它们,第二阶段“清除”会删除没有被标记的对象。尽管这个方法简单有效,但它可能导致内存碎片。
-
复制(Copying):
- 该算法将内存分为两个空间(通常称为“From”和“To”区域),在进行垃圾回收时复制所有活跃对象到另一个区域。复制后,原来的区域会被清空。该方法避免了内存碎片,但需要更多内存。
-
标记-整理(Mark-and-Compact):
- 该算法与标记-清除结合。首先,它会标记所有可达对象,然后将它们移动到一端,以消除内存碎片,并清理未使用的内存。
-
分代收集(Generational Collection):
- 根据对象的生命周期将内存分为若干代(通常为年轻代和老年代)。新创建的对象通常会在年轻代中变化较快,因此通过频繁回收年轻代来提高效率。而老年代中的对象较少变化,因此不需要频繁检查和回收。
JVM的垃圾回收机制:GC,是Java提供的对于内存自动回收的机制。
GC(Garbage Collection)是Java虚拟机(JVM)中的一项重要功能,用于自动管理堆内存中不再使用的对象,释放其占用的内存空间。GC通过标记和回收无效对象来实现内存的回收和释放,以避免内存泄漏和溢出。
下面是GC的主要工作原理和过程:
1、对象的标记:GC首先标记出所有活动对象,即仍然被引用或可达的对象。它从一组根对象开始,逐步遍历对象图,将可达的对象标记为活动对象,未标记的对象则被认为是无效的。
2、垃圾回收:在标记完成后,GC会对未标记的对象进行回收。具体的回收算法可以是不同的,常见的算法包括标记-复制算法、三色标记算法等。
3、内存压缩和整理:某些垃圾回收算法在回收完对象后,可能会产生内存碎片。为了优化内存使用,GC可能会进行内存压缩和整理操作,使得分配的对象在内存中连续存放,减少内存碎片的影响。
GC的优点是可以自动管理内存,减少了手动内存管理的复杂性,避免了内存泄漏和溢出的问题。但是,GC也会带来一定的性能开销。因此,在开发Java应用程序时,需要合理配置GC的参数和调整垃圾回收策略,以平衡性能和内存的使用。
3.JVM调优的方法?
1. 内存管理调优
-
堆内存大小设置:可以通过 (初始堆大小)和 (最大堆大小)参数来设置堆内存的大小。例如:。根据应用程序的需求来调整堆的大小
-
选择合适的垃圾回收器:不同的垃圾回收器适用于不同的场景,例如:
- G1垃圾回收器():适合大堆内存和低延迟的应用。
- Parallel垃圾回收器():适合高吞吐量的应用。
- CMS垃圾回收器():适合需要最小停顿的应用。
- ZGC和Shenandoah:适合需要极低延迟的应用,但需要JVM版本支持。
-
调整新生代和老年代的比例:可以通过 或 参数来调整新生代与老年代的比例,以优化对象的存活率和垃圾回收频率。
2. 垃圾回收调优
-
影响GC频率和停顿时间的参数:
MaxGCPauseMillis
:设置最大GC停顿时间。GC会尝试满足这个时间限制。GCTimeRatio
:调整应用可用时间与GC时间的比例。
-
监控和分析:使用JVM提供的工具(如JVisualVM、JConsole、GC日志)来监控和分析垃圾回收的情况,识别GC问题。
3. JIT编译优化
-
开启Tiered Compilation:使用,可以提高启动性能。
-
调整JIT编译行为:通过设置编译阈值,以控制何时将方法编译为机器代码。
-
4. 线程调优
-
线程栈大小:可以用参数设置每个线程的栈大小。例如:。适当调整以减少栈溢出和内存使用。
-
线程池管理:如果应用使用线程池,确保合理配置线程数,避免因过多线程上下文切换引起的性能问题。
5. 其他优化选项
-
类加载与内存优化:
- 使用 可保持对象引用的压缩,有助于减少堆内存的使用。
-
禁用或调整assertions:在生产环境中,可以禁用不必要的断言以提高性能:。
-da
6. 性能监控与分析
-
使用JVM监控工具:如Java Mission Control(JMC)和Flight Recorder来分析应用性能,查找瓶颈。
-
Profiling:使用工具(如YourKit、VisualVM等)进行代码剖析,找到性能开销较大的热点代码。
7. 配置文件与启动参数
- 合理配置启动参数:根据具体的应用场景和服务器配置调整JVM的启动参数,比如开启JIT、选择合适的GC、调整堆内存大小等。
4.堆和栈的区别?
1. 结构和用途:
-
栈(Stack):
- 栈是一种先进后出(LIFO, Last In First Out)的数据结构。
- 用于存储局部变量、函数参数和函数调用信息等。在函数调用时,函数的局部变量会压入栈中,函数返回时会将这些变量从栈中弹出。
- 每个线程都有自己的栈,用于跟踪函数调用和局部变量。
-
堆(Heap):
- 堆是一种基于动态存储分配的内存区域,没有特定的结构(可以认为是自由存储区)。
- 用于存储动态分配的对象和数据,如使用 关键字在C++中分配的对象,或在Java中创建的对象。
- 堆内存可以由程序员直接管理,程序员需要手动申请和释放内存,或者由垃圾回收机制(如Java、C#)自动管理。
2. 存储管理:
-
栈:
- 存储在栈内存中的数据管理简单,入栈和出栈操作的时间复杂度都是O(1)。
- 由于栈的大小是固定的(通常由操作系统限定),栈溢出(Stack Overflow)可能导致程序崩溃。
-
堆:
- 堆内存的大小通常只受到系统内存的限制,算法的复杂性和速度依赖于内存分配和回收的实现。
- 堆内存分配需要更多的时间,且容易出现内存泄漏或内存碎片问题。
3. 生命周期:
-
栈:
- 数据的生命周期由函数调用决定。函数结束后,栈上的数据会自动释放。
-
堆:
- 数据的生命周期由程序员控制,只有在不再需要对象时,才能显式地释放空间。如果没有释放,则会造成内存泄漏。
4. 访问速度:
-
栈:
- 由于其结构简单,内存访问速度较快,通常会比堆更快。
-
堆:
- 由于堆内存的管理要复杂得多,非顺序访问可能导致较慢的访问速度。
5.JVM使用命令
jvm 的一些命令_jvm命令-CSDN博客
jinfo
描述:输出给定 java 进程所有的配置信息。包括 java 系统属性和 jvm 命令行标记等。
jstat
jstat -gcutil <pid> <interval>
查看java进程的gc情况
以百分比显示每个区域的内存使用情况;
参数interval表示每多少毫秒刷新一次
6.class.forName和Classload的区别
1.相同点
两者都可以对类进行加载。
对于任意一个类/对象,我们都能够通过反射能够调用这个类的所有属性方法。
2.不同点
抽象类ClassLoader中实现的方法loadClass,loadClass只是加载,不会解析更不会初始化所反射的类。常用于做懒加载,提高加载速度,使用的时候再通过.newInstance()真正去初始化类。
7.谈谈JDK1.8特性有哪些
Lambda表达式 类似于ES6中的箭头函数
接口的默认方法和静态方法
新增方法引用格式
新增Stream类
新的日期API,Datetime,更方便对日期的操作
引入Optional,在SpringData中使用较多,然后再通过get获取值,主要用于防止NPE。
支持Base64
注解相关的改变
支持并行(parallel)数组
对并发类(Concurrency)的扩展。
JavaFX。
JDK1.8常用新特性_jdk1.8的新特性-CSDN博客
8.反射获取类中的所有方法和获取类中的所有属性
getDeclaredMethods()
和getDeclaredFields()
允许你访问类的所有方法和字段,包括私有的。getMethods()
和getFields()
只返回公共方法和字段,且包括继承的父类方法和字段。
9.懒汉和饿汉模式的区别?口述两种模式。
在饿汉式单例模式中,“饿” 体现的是一种急切的状态。就好像一个很饿的人,在看到食物(这里类比于单例对象)的时候,会迫不及待地先把食物拿到手(创建单例对象)。在这个模式下,单例对象在类加载阶段就被创建出来,而不是等到真正需要使用这个对象的时候才去创建。这种方式比较急切,所以被称为 “饿汉模式”。
在懒汉模式下,实例在第一次使用时才进行创建,因此称为“懒汉”,在需要被用的时候被创建,突出一个字“懒”
10.如何保证单例模式在多线程中的线程安全
私有构造函数:通过将构造函数设置为私有,我们禁止了外部类直接实例化Singleton类。这确保了只能通过getInstance()方法来获取实例。
volatile关键字:volatile关键字确保了instance变量的可见性。当一个线程修改了一个volatile变量的值时,其他线程能够立即看到这个变化。这样可以避免线程之间的缓存不一致问题。
双重检查锁定(Double-Checked Locking):为了减少同步开销,我们在第一次检查instance是否为空后,才进入同步块。这是因为大多数情况下,实例已经被创建,不需要进入同步块。只有在第一次创建实例时才会需要同步。
同步块:在同步块内部,我们再次检查instance是否为空,以确保只有一个线程能够创建实例。这是为了防止多个线程同时通过了第一个空检查并尝试创建多个实例。
11.spring运用了什么设计模式?讲一下哪些部分运用到了这些设计模式
- 一、简单工厂模式(Simple Factory)
-
定义:
简单工厂模式:并不属于 GoF(四人组)总结的 23 种设计模式,但它却在实际开发中被频繁使用。其核心是 由一个工厂类根据传入的参数,动态决定创建哪一个产品类的实例。
举例:
Spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得 Bean 对象。但是,在传入参数后创建 Bean 还是传入参数前创建 Bean,这个要根据具体情况而定。
- 二、工厂方法模式(Factory Method)
-
定义:
工厂方法模式:定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类。
举例:
Spring 使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 Bean 对象。两者对比如下:
BeanFactory:延迟注入(使用到某个 Bean 的时候才会注入),相比于 ApplicationContext 来说会占用更少的内存,程序启动速度更快。
ApplicationContext:容器启动的时候,不管你用没用到,一次性创建所有 Bean。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory,除了有 BeanFactory 的功能还有额外更多功能,所以一般开发人员使用 AplicationContext 更多。 - 三、单例模式(Singleton)
-
定义:
单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
举例:
在我们系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如;程序的行为异常、资源使用过量、或者不一致性的结果。
- 四、适配器模式(Adapter)
-
定义:
适配器模式:将一个接口转换成客户希望的另一个接口,适配器使接口不兼容的那些类可以一起工作。
举例:
Spring AOP 中的适配器模式:
我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是 AdvicorAdapter。
- 五、代理模式(Proxy)
-
定义:
代理模式:为其他对象提供一个代理以控制对这个对象的访问。
举例:
代理模式在 AOP 中的应用:
AOP(Aspect-Oriented Programming,面向切面编程):能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如:事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性
- 七、观察者模式(Observer)
-
定义:
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。
举例:
Spring 事件驱动模型就是观察者模式很经典的一个应用。Spring 事件驱动模型非常有用,在很多场景都可以解耦我们的代码。比如我们每次添加商品的时候都需要重新更新商品索引,这个时候就可以利用观察者模式来解决这个问题
- 八、策略模式(Strategy)
-
定义:
策略模式:定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法独立于使用它的客户。
举例:
Spring 框架的资源访问 Resource 接口。该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。
- 九、模板方法模式(Template Method)
-
定义:
模板方法模式:在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
举例:
Spring 中 JdbcTemplate、HibernateTemplate 等以 Template 结尾的对接入内容进行操作的类,它们就使用到了模板方法。一般情况下,我们都是使用继承的方法来实现模板模式,但是 Spring 并没有使用这种方式,而是使用 Callback 模板与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。
12.说说你都知道哪些设计模式?最常用的有哪些?总共有几种设计模式?
23种设计模式 - 知乎
13.redis可以储存哪几种数据类型?你的项目里都用redis存储哪些数据
14.redis有哪些常用操作?(命令)
15.redis缓存和数据库怎么保持一致?
16.redis可以持久化么?如何持久化?
17.redis分布式锁怎么使用?
18.redis缓存数据丢失怎么使用?
19.redis如果崩溃了如何快速恢复?
20.redis是如何部署的?是单个部署还是集群部署?为什么这么做?
21.什么是缓存穿透、缓存击穿、缓存雪崩?如何解决?
22.List和set和map的区别?
23.arrarylist,linkedlist;arraylist内部扩容机制是怎样的?
24.hashmap扩容机制,HashMap的底层原理
25.hashmap的底层用什么储存的?线程是否安全?
26.final和finally和finalize的区别
27.String、StringgBuilder、StringBuffer的区别
28.重写equals已经能比较两个对象了,为什么还要重写hashcode方法
29.基本数据和包装数据类型的区别?
30.什么是多态?举个例子
31.抽象类和接口的区别
抽象类是半抽象的,有构造方法,抽象类可以包含抽象方法和非抽象方法,也可以有成员变量(不仅仅是常量)
类和类之间只能单继承,一个抽象类只能继承一个类(单继承)
接口是完全抽象的,没有构造方法,接口和接口之间支持多继承,一个类可以同时实现多个接口
这在Java 8之前基本正确,但在Java 8及之后,接口可以包含默认方法和静态方法
32.java中的几种基本类型,各占用多少字节?
33.运行时异常有哪些?
34.对面向对象的理解?面向对象有的特征有哪些?
35.HTTP报文有哪几部分组成?Get/Post有什么区别
36.转发(forward)和重定向(redirect)的区别
37.什么是AOP?什么是IOC?
38.AOP用到什么设计模式
39.SpringBean的生命周期
40.SpringIOC是怎么实现了依赖注入,有几种依赖注入方式?
41.SpringMVC的执行流程
42.SpringMVC与springboot的区别?
43.SpringMVC的事物有过哪几种?怎么处理的?
44.SpringBoot在初始化以前执行一个方法,应该怎么做?初始化以后在执行一个方法应该怎么做,有哪几种方法去做
45.在项目中会经常用到哪些注解?讲出十个
46.@Autowired注入和自己new一个对象有什么区别?
47.myBaties防sql注入的操作
48.Mybaties都要用到哪些标签
49.#和$的区别
#原样输出
$占位符