java个人学习

存储结构

ArrayList底层使用的是数组 LinkedList使用的是链表。
ArrayList 初始化大小10,扩容1.5倍。JDK8默认初始化0
Vector     初始化大小10,扩容2倍
hashMap jdk7是一开就创建容量为16 jdk8是先创建{}当调用put方法时才初始化容量为16 扩容2倍
hashtable 初始化大小11,扩容2n+1倍,Hashtable 是线程安全的,所有的元素操作都是 synchronized 修饰的,性能较差.如果要线程安全又要保证性能,建议使用 JUC 包下的 ConcurrentHashMap
Hashset   初始化大小16,扩容2倍

ArrayList 的不安全主要体现在两个方面。
多线程环境下add可能一个线程会覆盖另一个线程的值
ArrayList 默认数组大小为 10。add AB线程可能出现数组还没有扩容,最大的下标才为 9,所以会抛出数组越界异常 ArrayIndexOutOfBoundsException;
用Corrections工具类下的synchronizedList()

在JDK7之前,hashmap底层采用数组+链表的数据结构来存储数据 头插法
JDK8以后hashmap的底层数据结构由数据+链表+红黑树实现、jdk8以后插入数据采用尾插法
长度大于或等于8时,会将链表转化为红黑树 在进行remove操作的时候,当红黑树的节点个数小于或者等于6时,会将红黑树转化为链表
初始化数组长度为 16,元素数量大于 12 时, 就会进行扩容
HashMap线程不安全问题体现在哪
1. 多线程put导致元素丢失
2. put和get并发时,可能导致get为null
3. 1.7多线程下扩容死循环

Jdk 1.7 之前 ConcurrentHashMap 通过加锁保证线程安全,并引入锁分段机制以减小加锁的粒度,从而提升性能。Jdk 1.8 中的 ConcurrentHashMap 实现则引入了 CAS 机制以尽量避免加锁操作

HashTable
底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
初始size为11,扩容:newsize = olesize*2+1
计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length


HashMap
底层数组+链表实现,可以存储null键和null值,线程不安全
初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
计算index方法:index = hash & (tab.length – 1)

ConcurrentHashMap
底层采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

Hashset
HashSet不能保证元素的存取顺序一致
不能有重复的元素
HashSet线程不安全

没有带索引的方法,所以不能通过普通for循环进行遍历
分析HashSet的扩容和转成红黑树的机制
HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是 16*加载因子(loadFactor)是0.75 = 12
如果table数组使用到了临界值12,就会扩容到 16 * 2 = 32,新的临界值就是 32 * 0.75 = 24
在java8中,如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是8),并且table的大小 >= MIN_TREEIFY_CAPACITY(默认是64),就会进行树化(转成红黑树),否则仍然采用数组扩容机制。

AQS模型

AbstractQueuedSynchronizer抽象队列同步器:AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架。AQS维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)抽象队列同步器中包括两个部分:临时资源和一个FIFO的CLH阻塞队列.

AQS的核心思想
如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资
源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。内部使用AQS的例子:以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

CAS

即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置V、预期原值A和新值B。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。

解决 ABA
1.解决 ABA 问题的一种方法是使用带版本号的 CAS,也称为双重 CAS(Double CAS)或者版本号 CAS。具体来说,每次进行 CAS 操作时,不仅需要比较要修改的内存地址的值与期望的值是否相等,还需要比较这个内存地址的版本号是否与期望的版本号相等。如果相等,才进行修改操作

2.使用原子类 

volatile

可见性、禁止指令重排、内存屏障

Volatile的作用:具有多线程间内存可见性,禁止指令重排序,但不具备原子性,在我们的认知中,多线程场景下通常使用锁来实现,但是Java为我们提供了Volatile关键字,在某些场景下比锁使用的更加方便。

JMM内存模型

IO多路复用

应用程序发起的一次IO操作包含两个阶段:

  • IO调用:应用程序进程向操作系统内核发起调用。
  • IO执行:操作系统内核完成IO操作。

操作系统内核完成IO操作还包括连个两个过程:

  • 准备数据阶段:内核等待I/O设备准备好数据
  • 拷贝数据阶段:将数据从内核缓冲区拷贝到用户空间缓冲区

select有几个缺点:

  • 监听的IO最大连接数有限,在Linux系统上一般为1024。
  • select函数返回后,是通过遍历fdset,找到就绪的描述符fd。(仅知道有I/O事件发生,却不知是哪几个流,所以遍历所有流)

因为存在连接数限制,所以后来又提出了poll。与select相比,poll解决了连接数限制问题。但是呢,select和poll一样,还是需要通过遍历文件描述符来获取已经就绪的socket。如果同时连接的大量客户端在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降

epoll 直接将 fd 集合维护在内核中,通过红黑树来高效管理 fd 集合,同时维护一个就绪列表,当 fd 就绪后会添加到就绪列表中,当应用空间调用 epoll_wait 获取就绪事件时,内核直接判断就绪列表即可知道是否有事件就绪。

优点
解决了 select 和 poll 的缺点,高效处理高并发下的大量连接,同时有非常优异的性能。

缺点
跨平台性不够好,只支持 linux,macOS 等操作系统不支持,相较于 epoll,select 更轻量可移植性更强、在监听连接数和事件较少的场景下,select 可能更优

Redis

8种内存淘汰策略

供应用程序选择,用于我遇到内存不足时该如何决策:
1、noeviction(默认):不进行淘汰数据。一旦缓存被写满,再有写请求进来,Redis就不再提供服务,而是直接返回错误。Redis 用作缓存时,实际的数据集通常都是大于缓存容量的,总会有新的数据要写入缓存,这个策略本身不淘汰数据,也就不会腾出新的缓存空间,我们不把它用在 Redis 缓存中。
2、volatile-ttl:在设置了过期时间的键值对中,移除即将过期的键值对。
3、volatile-random:在设置了过期时间的键值对中,随机移除某个键值对。
6、allkeys-random:在所有键值对中,随机移除某个key。

4、volatile-lru:在设置了过期时间的键值对中,移除最近最少使用的键值对。
5、volatile-lfu:在设置了过期时间的键值对中,移除最近最不频繁使用的键值对
7、allkeys-lru:在所有的键值对中,移除最近最少使用的键值对。
8、allkeys-lfu:在所有的键值对中,移除最近最不频繁使用的键值对

1、 corePoolSize 线程池核心线程大小
corePoolSize 是核心线程数,线程池中的一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。
2、maximumPoolSize 线程池最大线程数
线程池能够容纳同时执行的最大线程数,此值大于等于1。
3、keepAliveTime 多余的空闲线程存活时间
当线程空闲时间达到keepAliveTime值时,多余的线程会被销毁直到只剩下corePoolSize个线程为止。默认情况下:只有当线程池中的线程数大于corePoolSize时keepAliveTime才会起作用,直到线程中的线程数不大于corepoolSIze,
4、unit 空闲线程存活时间单位
keepAliveTime的计量单位
5、workQueue 工作队列
任务被提交给线程池时,会先进入工作队列,任务调度时再从工作队列中取出。
6、threadFactory 线程工厂
创建一个线程工厂用来创建线程,可以用来设定线程名、是否为daemon线程等等
7、handler 四种拒绝策略
AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常。(默认这种)
DiscardPolicy:丢弃任务,但是不抛出异常
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) 。也就是当任务被拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务从队尾添加进去,等待执行。
CallerRunsPolicy:谁调用,谁处理。由调用线程(即提交任务给线程池的线程)处理该任务,如果线程池已经被shutdown则直接丢弃略

一个任务被提交到线程池以后,首先会找有没有空闲并且存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会放到工作队列中,直到工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。工作队列满,且线程数等于最大线程数,此时再提交任务则会调用拒绝策略

spring7种事务  

1、PROPAGATION_REQUIRED(默认
支持当前事务,如果没有则创建一个新的
2、PROPAGATION_SUPPORTS
支持当前事务,如果没有则不使用事务
3、PROPAGATION_MANDATORY
支持当前事务,如果没有事务则报错
4、PROPAGATION_MANDATORY
新建一个事务,同时将当前事务挂起
5、PROPAGATION_NOT_SUPPORTED
以无事务的方式执行,如果当前有事务则将其挂起
6、PROPAGATION_NEVER
以无事务的方式执行,如果当前有事务则报错
7、PROPAGATION_NESTED
如果当前有事务,则在当前事务内部嵌套一个事务,内部事务的回滚不影响当前事务。如果当前没有事务,就相当于REQUIRED

srpingboot启动原理

一、运行 SpringApplication.run() 方法
new springApplication()调用构造方法
 1.确定应用程序类型
利用WebApplicationType.deduceFromClasspath()方法判断当前应用程序的容器,默认使用的是Servlet 容器,除了servlet之外,还有NONE 和 REACTIVE (响应式编程);
 2.加载所有的初始化器
 3.加载所有的监听器
从META-INF/spring.factories,自带有2个,分别在源码的jar包的 spring-boot-autoconfigure 项目 和 spring-boot 项目里面各有一个
org.springframework.context.ApplicationContextInitializer 接口就是初始化器了
org.springframework.context.ApplicationListener  接口就是监听器了
这两个作用(https://blog.csdn.net/weixin_46666822/article/details/124619117)
 4.设置程序运行的主类
deduceMainApplicationClass(); 这个方法仅仅是找到main方法所在的类,为后面的扫包作准备,deduce是推断的意思,所以准确地说,这个方法作用是推断出主方法所在的类;
二、开启计时器
// 实例化计时器
StopWatch stopWatch = new StopWatch(); 
// 开始计时
stopWatch.start();
将java.awt.headless设置为true 没有显示器器和鼠标键盘的模式下照样可以工作
三、.获取并启用监听器
getRunLiisteners(args)
创建所有 Spring 运行监听器并发布应用启动事件
启用监听器
四、设置应用程序参数
将执行run方法时传入的参数封装成一个对象
五、准备环境变量
准备环境变量,包含系统属性和用户配置的属性,执行的代码块在 prepareEnvironment 方法内将maven和系统的环境变量都加载进来
六、打印 banner 信息
自定义banner信息 在resources目录下添加一个 banner.txt 的文件即可
七、创建应用程序的上下文
调用 createApplicationContext() 方法创建应用程序的上下文
八、实例化异常报告器
异常报告器是用来捕捉全局异常使用的,当springboot应用程序在发生异常时,异常报告器会将其捕捉并做相应处理,在spring.factories 文件里配置了默认的异常报告器,
这个异常报告器只会捕获启动过程抛出的异常
九、准备上下文环境
 1.实例化单例的beanName生成器,在 postProcessApplicationContext(context); 方法里面。使用单例模式创建了BeanNameGenerator 对象,其实就是beanName生成器,用来生成bean对象的名称
 2.执行初始化方法 其实是执行加载出来的所有初始化器,实现了ApplicationContextInitializer 接口的类
 3.将启动参数注册到容器中 这里将启动参数以单例的模式注册到容器中,是为了以后方便拿来使用,参数的beanName 为 :springApplicationArguments
十、刷新上下文
 调用spring的refresh加载IOC容器、自动配置类,并创建bean、servlet容器等信息
https://blog.csdn.net/m0_63437643/article/details/123089891
十一、刷新上下文后置处理
afterRefresh 方法是启动后的一些处理,留给用户扩展使用,目前这个方法里面是空的
十二、结束计时器
十三、发布上下文准备就绪事件
十四、启动完成

@SpringBootApplication注解的代码如下,这些注解中 有关SpringBoot的注解只有三个,分别是:
SpringBootConfiguration
EnableAutoConfiguration
ComponentScan()

自动装载
实际上@SpringBootApplication这个注解是这三个注解的复合注解。每次写三个会显得极其麻烦,将其整合。
首先、@SpringBootConfiguration等同于@Configuration,带有spring的标志,是属于spring的一个配置类;
其次、@EnableAutoConfiguration是开启自动配置功能;
@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器
@SpringFactoriesLoader详解,其主要功能就是从指定的配置文件 META-INF/spring.factories 加载配置,spring.factories 是一个典型的 java properties 文件,配置的格式为 Key=Value 形式,只不过 Key 和 Value 都是 Java 类型的完整类名
@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些 bean 定义加载到容器中。

Mysql

回表 索引覆盖 索引下推  
通过非主键索引查询数据时,我们先通过非主键索引树查找到主键值,然后再在主键索引树搜索一次(根据rowid再次到数据块里取数据的操作),这个过程称为回表
当SQL语句的所有查询字段(select列)和查询条件字段(where子句)全都包含在一个索引中,便可以直接使用索引查询而不需要回表,故称索引覆盖
查询字段(select列)不是/不全是联合索引的字段,查询条件为多条件查询且查询条件子句(where/order by)字段全是联合索引。MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表字数。索引下推  

回表
根据上面的索引结构说明,主键索引和普通索引的查询区别
如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表
覆盖索引
如果执行的语句是 select ID from T where k between 3 and 5,这时只需要查 ID 的值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 k 已经“覆盖了”我们的查询需求,我们称为覆盖索引。

2.什么是事务
事务(Transaction)是访问并可能更新数据库中各项数据项的一个程序执行单元(unit)
3.事务的四大特性acid

事务有四个特性:acid,原子性,一致性,隔离性,持久性。

原子性:事务的原子性指的是一个事务中的所有操作是不可分割的,必须是同一个逻辑单元,要么全部执行,要么全部不执行

一致性:事务前后数据的完整性保持一致

隔离性:多个事务在同时触发的时候,彼此互不干扰,当事务出现并发操作的时候要相互隔离

持久性:事务一旦提交,对数据库的产生的效果是永久的,其他事务的执行或者故障都不会对本次事务的操作结果有影响

事务的四个性质是怎么实现的?

原子性:原子性依靠undolog回滚日志实现,每次对数据做修改或者删除插入的操作都会生成一条undolog来记录操作之前的数据状态,使用rollback的语句能够将所有执行成功的SQL语句产生的效果撤销。回滚日志先于数据持久化到硬盘上。回滚就是逆向操作。

一致性:依靠其他三个性质实现,一致性指的是数据的完整性,为了保证数据的有意义状态

隔离性:通过锁机制实现,当事务操作数据的时候加锁,让事务执行前后看到的数据时候一致的,并行执行事务和串行执行事务产生的效果一样。本质有两种,悲观锁和乐观锁

持久性:持久性通过redolog重做日志实现,redolog记录的是对数据库的操作。MySQL先把存放在硬盘上的数据加载到内存中,在内存中做修改再刷回磁盘,redolog使得在事务提交的时候将数据刷回磁盘。

当数据库宕机之后,先通过redolog来恢复数据库,然后根据undolog和binlog的记录决定是要回滚还是提交

4、四种隔离级别

读未提交:在这种隔离级别下,别的事务在提交之前对数据进行修改,所有事务能够读到修改后的数据。读取未提交的数据会导致脏读,脏读指的是如果事务A读到事务B修改后的数据,但是事务B执行的回滚操作,那么事务A读到的数据就是脏数据。读未提交的隔离级别几乎不会用到。可能会出现脏读、不可重复读和幻读等问题

读提交:只有别的事务提交之后,本事务才能看到修改后的数据,提交之前只能看到未修改的数据。这是大多数数据库默认的隔离级别,但是不是MySQL的默认隔离级别。导致不可重复读:在当前事务中只能看见已经提交事务的执行结果,当同一事务在读取期间出现新的commit操作,读到不一样的数据,可能出现不可重复读和幻读问题

可重复读:可重复读是MySQL的默认隔离级别。 对于同一个字段事务在执行过程中读到数据,除非自己修改。保证当前事务不会读取到其他事务的update操作,因此会导致幻读。

幻读指的是select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

序列化:串行化,对于同一行记录,读会加读锁,写会加写锁,出现读写冲突的时候,后访问的事务必须等前一个事务执行完成才能继续执行。

通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

Eureka工作流程

1)Eureka Server 启动成功,等待服务注册,每个Eureka Server 都存在独立完整的服务注册表信息,注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等。
2)Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务
3)Eureka Client 会每 30s 向 Eureka Server 发送一次心跳请求,证明客户端服务正常(服务续约,获取注册列表信息)
4)当 Eureka Server 90s 内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例(服务剔除)
5)单位时间内 Eureka Server 统计到有大量的 Eureka Client 没有上送心跳(15%),则认为可能为网络异常,进入自我保护机制,不再剔除没有上送心跳的客户端
6)当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式
7)Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存到本地
8)服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存(看源码是没有这部分功能的 同步注册表依靠心跳)
9)Eureka Client 获取到目标服务器信息,发起服务调用
10)Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除(服务下线)
11)Eureka Server 之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server 都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。

当有消息发送到mq  先将消息写入到内存 然后将消息第储到commitLog文件(topic queueId message)无序的  同时commitlog会将消息分发给两个索引文件(consumerQueue ,indexFile)consumerQueue记录的是队列的消息消费程度 为了保证效率它不记录commitlog里面的消息内容 只存commitlog里面的索引   indexFile文件是在consumerQueue基础之上提供的一个索引记录功能文件  

RocketMQ事务消息流程概要

分为两个流程:正常事务消息的发送及提交、事务消息的补偿流程。
1.事务消息发送及提交:
(1) 发送消息(half消息)。
(2) 服务端响应消息写入结果。
(3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)。
(4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)
2.补偿流程:
(1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”
(2) Producer收到回查消息,检查回查消息对应的本地事务的状态
(3) 根据本地事务状态,重新Commit或者Rollback
其中,补偿阶段用于解决消息Commit或者Rollback发生超时或者失败的情况。

如果消费者消费失败或超时则可以进行重试机制保证消费者消费成功

GC

首先需要知道,GC又分为minor GC 和 Full GC(major GC)。Java堆内存分为新生代和老年代,新生代
中又分为1个eden区和两个Survior区域。一般情况下,新创建的对象都会被分配到eden区,这些对象经过一个minor gc后仍然存活将会被移动到Survior区域中,对象在Survior中每熬过一个Minor GC,年龄就会增加一岁,当他的年龄到达一定程度时,就会被移动到老年代中。

当eden区满时,还存活的对象将被复制到survior区,当一个survior区满时,此区域的存活对象将被复制到另外一个survior区,当另外一个也满了的时候,从前一个Survior区复制过来的并且此时还存活的对象,将可能被复制到老年代。

Minor GC:从年轻代回收内存,当jvm无法为一个新的对象分配空间时会触发Minor GC,比如当Eden区满了。当内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden和Survior区不存在内存碎片写指针总是停留在所使用内存池的顶部。执行minor操作时不会影响到永久代,从永久代到年轻代的引用被当成GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉(永久代用来存放java的类信息)。如果eden区域中大部分对象被认为是垃圾,永远也不会复制到Survior区域或者老年代空间。如果正好相反,eden区域大部分新生对象不符合GC条件,Minor GC执行时暂停的线程时间将会长很多。Minor may call “stop the world”;
Full GC:是清理整个堆空间包括年轻代和老年代。那么对于Minor GC的触发条件:大多数情况下,直接在eden区中进行分配。如果eden区域没有足够的空间,那么就会发起一次Minor GC;对于FullGC的触发条件:如果老年代没有足够的空间,那么就会进行一次FullGC在发生MinorGC之前,虚拟机会先检查老年代最大可利用的连续空间是否大于新生代所有对象的总空间。如果大于则进行Minor GC,如果小于则看HandlePromotionFailure设置是否是允许担保失败(不允许则直接FullGC)如果允许,那么会继续检查老年代最大可利用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试minor gc (如果尝试失败也会触发Full GC),如果小于则进行Full GC。但是,具体什么时候执行,这个是由系统来进行决定的,是无法预测的。

JVM

运行时数据区(程序计数器(PC寄存器)、方法区、堆、java栈、本地方法栈)
程序计数器(线程私有)
程序计数器的主要作用:
1.字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
2.在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时能够知道该线程上次运行到哪⼉了。(上下文切换)
注意:程序计数器是唯一一个不会出现OutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。(因为程序计算器仅仅只是一个运行指示器,它所需要存储的内容仅仅就是下一个需要待执行的命令的地址)

Java虚拟机栈(线程私有)
J局部变量表、操作数栈、动态链接、方法出口信息

本地方法栈
局部变量表、操作数栈、动态链接、出口信息。

Java堆(线程共享)
对象
当java堆上没有内存完成实例分配,并且堆大小也无法扩展是,将会抛出OutOfMemoryError异常。Java堆是垃圾收集器管理的主要区域。

方法区(线程共享)
类信息、常量、静态变量、即时编译器编译后的代码等数据运行时数据区(程序计数器(PC寄存器)、方法区、堆、java栈、本地方法栈)
虚拟机遇到一条new指令时,先检查常量池是否已经加载过相应的类,如果没有,必须先执行相应的类加载
类加载过后,分配内存,若java对中南内存时绝对规整的,使用“指针碰撞”方式分配内存;如果是不规整的,就从空闲列表中分配
划分内存时需要考虑并发问题,两种方式:CAS同步处理,或者本地线程分配缓冲。
内存空间初始化操作,接着是做一些必要的对象设置(元信息、哈希码。。。。。。),后执行方法
为对象分配内存
类加载完成后,接着会在java堆中划分一块内存分配给对象。内存分配根据java堆是否规整,有两种方法
指针碰撞:如果java堆的内存时规整的,即所有用过的内存放在一边,而空闲的放在一边。分配内存时将位于中间的指针指示器向空闲内存移动一段与对象大小相等的距离,这样便完成分配内存工作
空闲列表:如果java堆的内存时不规整的,则需要由虚拟机维护一个列表来记录那些内存是可用的,这样在分配的时候可以从列表中查询到足够大的内存分配给对象,并在分配后更新列表
选择那种分配方法时由java堆是否规整来决定的,而Java堆是否规整又由才用的垃圾收集器是否带有整理功能决定

垃圾回收器
一、常见的垃圾收集器有串行垃圾回收器(Serial)、并行垃圾回收器(Parallel)、并发清除回收器(CMS)、G1回收器。
垃圾回收算法种类
复制(coping)算法;(发生在eden区和两个Survior区域)
标记-清除(Mark-Sweep)算法;(发生在老年代)
标记-整理(Mark-Compact)算法。(发生在老年代)

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值