垃圾回收的相关感念

一、System.gc()的理解

调用会触发Full GC,进行垃圾回收释放内存,无法保证对垃圾收集器的调用,无法确定执行时间。

二、内存溢出和内存泄漏

内存溢出(OOM):没有空间内存,垃圾回收器也无法提供更多的可用内存。可能是堆内存设置不够或者是代码问题。在OOM之前一般情况下都会调用一次GC,当GC后空间还是不足才抛出OOM异常,当然也不是任何情况都调用,当出现一个超大对象,并且超过堆的最大值,那么JVM判断GC也无法解决问题,直接抛出OOM。

内存泄漏(Memory Leak):只有对象不再被使用了,但是GC又无法回收他们,才叫内存泄漏。但是某些时候,由于编写习惯不好,导致某些对象的生命周期很长,那么也可以成为内存泄漏。内存泄漏积累起来,可能会导致内存溢出。

  1. 静态集合类
  2. 单例模式
  3. 内部类持有外部类
  4. 各种连接,如数据库连接、网络连接、IO连接
  5. 变量不合理的作用域
  6. 改变哈希值
  7. 缓存泄漏,使用WeakHashMap,弱引用fa x
  8. 监听器和回调

三、Stop The World

STW:发生GC的时候,会导致用户线程停顿,导致应用程序没有任何相应,进入卡死状态。因为垃圾回收的时候需要确定GC Roots,如果GC Roots不能变,所以GC的时候就需要STW,其实就是确保数据的一致性。所有的GC实践都会导致STW,只能说缩短STW的时间和减小出现频率。

四、垃圾回收的并行和并发

1、程序中的并行和并发

并发(Concurrent):在一个时间段中,已启动和完毕之间,且这几个程序都在一个处理器上运行。一个CUP快速切换多个任务,并不是真正的同时执行,只不过依赖于CPU的快速切换

并行(Parallel):当多个CPU核心的时候,一个CPU核心执行一个进程在执行。

二者的对比:

并发:在一个时间段内发生了,相互之间抢占资源

并行:在一个时间点上发生了,相互之间不抢占资源

2、垃圾回收期中并行和并发

并行和串行(针对垃圾回收线程)

并行:多个垃圾回收线程并行工作、吞吐量大

串行:一个垃圾线程工作

并发和独占式(针对垃圾回收线程是否和用户线程一起执行)

并发:垃圾回收线程和用户线程同时执行 (CMS、G1)、低延迟

五、安全点与安全区域

安全点:程序在执行的时候,并非所有的时间点都能停止,只有在特定的位置才能停下来,这时候才能执行GC。安全点太少则GC时间很长,太多则影响性能。选择规则:是否具有让程序长时间执行的特征。

如何在GC发生时,检查所有的线程都跑到最近的安全点停顿下来呢?

  • 抢先式中断:首先中断所有线程,如果线程不在安全点,就恢复,让线程跑到安全点。
  • 主动式中断:设置一个中断标志位,各个线程运行到安全点的时候主动轮训这个标志位,判断是否需要挂起

安全区域:因为线程存在不执行的时候,无法响应JVM的中断请求。一个代码片段,对象的引用关系不会发生变化,则可以设置为安全区。

执行过程:

  • 当线程运行到安全区,首先标识已经进入安全区,如果发生GC的时候,JVM会忽略该线程的状态
  • 当线程即将离开安全区,会检查JVM是否完成GC,如果完成了,则继续运行,否则线程必须等待直到收到可以离开信号为止。

六、再谈引用

当内存空间还足够的时候,则保留,如果内存空间进行垃圾回收后还很紧张,就抛弃这些对象。

1、强引用 - 永远不回收

最常用的引用,无论什么情况,只要强引关系存在,GC就不会回收被引用的对象。平时使用的99%情况就是强引用。

Object obj = new Object(); obj就是强引用。强引用对象是可达的,GC永远不会回收。所以强引用也是java内存泄漏的主要原因之一。

2、软引用 - 内存不足则回收

内存溢出之前,将会把这些对象放入会后的范围之中,进行二次回收(第一次回收是垃圾)。缓存数据,保证正确执行(mybatis中就有一些结构使用软引用)。下面的代码需要把强引用消除,弱引用才能生效。

3、弱引用 - 只要GC就回收

只要GC,就会回收,跟软应用一样,可以用作缓存。

回收的时候相对更容易、速度更快,因为不需要任何判断。

WeakHashMap -> 可以使用缓存,因为使用弱引用,避免了强引用OOM问题。

4、虚引用 - GC通知

对象虚引用是否存在,不影响对象,也不会影响其生命周期,唯一目的就是能在这个对象被GC后接受到一个系统通知,也就是用于对象GC跟踪的。通过虚引用也无法获取到对象。

5、终结器引用

实现finalize()方法,也可以成为终结器引用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在软件开发中,常量是指一些值在程序运行期间保持不变的变量。与变量不同,常量的值不能在运行时更改,通常在代码中以明确的方式指定,并且在程序的执行过程中始终保持不变。 以下是一些常见的常量的例子: 1. 数学常量:在计算机程序中,常常需要使用数学常量,例如π(圆周率)、e(自然常数)等。这些常量通常定义为全局常量,可以在整个程序中使用。 ```python # 在Python中,使用内置的数学库定义常量 import math PI = math.pi E = math.e ``` 2. 颜色常量:在图形应用程序中,常常需要使用颜色常量来指定对象的颜色,例如红色、绿色、蓝色等。这些常量通常定义为枚举类型或预定义的整数值。 ```csharp // 在C#中,可以使用枚举类型来定义颜色常量 enum Color { Red, Green, Blue } // 在使用时,可以直接引用枚举值 Color c = Color.Red; ``` 3. 字符串常量:在程序中,常常需要使用一些字符串常量,例如程序名称、版本号等。这些常量通常定义为全局常量或预处理指令。 ```c // 在C语言中,可以使用预处理指令#define来定义常量 #define PROGRAM_NAME "My Program" #define VERSION "1.0.0" // 在使用时,可以直接引用常量名 printf("%s version %s\n", PROGRAM_NAME, VERSION); ``` 总之,常量是在程序运行时保持不变的变量,可以提高代码的可读性、可维护性和可重用性。在编写程序时,应该合理地使用常量,以减少硬编码并提高程序的灵活性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值