[知乎]Java 语言的 GC 为什么不实时释放内存?

知乎问题 Java 等语言的 GC 为什么不实时释放内存?

下面是RednaxelaFX的回答:

1.最基本的纯引用计数方式的自动内存管理可以做到实时释放死对象,但却无法处理存在循环引用的对象图的释放。

这个问题一定程度上可以通过引入弱引用的概念来解决,但通用的能处理带循环引用对象图的引用计数都是有别的管理方式备份的(通常是某种tracing GC,例如mark-sweep;也有名为“trial-deletion”的循环检测方法,但这个通常比tracing性能更差所以用得较少),例如CPython使用以引用计数为主、mark-sweep为辅的方式,Adobe Flash的ActionScript VM 2(AVM2)也是以延迟引用计数(DRC)为主、增量/保守式mark-sweep为辅。反之,像C++的std::shared_ptr就是纯引用计数,无法靠自己处理带循环引用的对象图,而必须靠程序员自己小心使用,在必要的地方用std::weak_ptr来破除循环;CPython在2.0之前也使用纯引用计数,无法处理循环引用,只能等着泄漏内存。既然通用的引用计数还得用tracing GC来备份,实现这样的自动内存管理等于得实现两份,想偷懒的话还不如一开始就只实现某种tracing GC,例如mark-sweep。

2.最基本的纯引用计数方式对引用计数器的操作非常频繁,这里有额外开销,至于是否严重到成问题就看具体应用的可忍受程度。在内存充裕的前提下,基本的tracing GC比基本的引用计数方式的性能更好(特别是从throughput角度看),不需要做冗余的计数器更新。同时,在多线程环境下引用计数器可能成为线程间共享的数据,需要做同步保护(这里把原子更新算同步保护的一种),这也是个额外开销的来源;因为tracing GC不需要维护引用计数器所以也就没有这种同步的开销。引用计数的这些性能缺点可以通过一些高级变种来缓解,例如前面提到AVM2的延迟引用计数,只记录堆上对象之间的引用计数而不记录栈上(主要是表达式临时值)对对象的引用计数,以此减少对计数器的更新次数来提高性能。详情可参考文档:MMgc | MDN。这些引用计数的高级变种通常意味着一定程度的延迟释放,跟楼主想实时释放的初衷就不符了。另一方面,虽然最基本的tracing GC会有较长的延迟,但它们也有高级变种,可以并行、并发、增量式执行,降低延迟;也有办法实现thread-local GC来应对像是“请求-响应”式的Web应用批量释放一个线程临时分配的对象的需求。

3.如果选用tracing GC来实现自动内存管理,它是不显式维护对象的引用计数的,也就没有“引用计数到0”的概念。所以基于tracing GC的JVM或其它语言的运行时环境自然不会“引用计数到0就释放对象”。

4.引用计数方式其实也有经典的卡顿情况。例子之一就是一个对象个数很多、引用链很长的对象图假如只是被一个引用而留活,那么那个引用一死就会引发大量对象扎堆释放(但却不是“批量释放”,开销不同),这一样会引起卡顿。单纯讨论最坏情况的话其实引用计数也有这样糟糕的一面。纯人工的malloc()/free()或new/delete可以让程序员人肉找出生命周期相同的对象,然后利用诸如arena之类的方式为它们分配内存,就可以它们死的时候真正批量释放掉它们,这样就很高效;但纯引用计数却不是这么回事。使用引用计数会否遇到这种卡顿全看你的程序里对象图的引用关系是怎样的。

时间: 2024-11-10 16:50:12

[知乎]Java 语言的 GC 为什么不实时释放内存?的相关文章

Java初学者必知:Java语言的11大特点

Java是一种简单的,面向对象的,分布式的,解释型的,健壮安全的,结构中立的,可移植的,性能优异.多线程的静态语言.那么java语言的特点是什么呢? 1.Java语言是简单的 Java 语言的语法与C语言和C++语言很接近,使得大多数程序员很容易学习和使用Java.另一方面,Java丢弃了C++中很少使用的.很难理解的.令人迷惑 的那些特性,如操作符重载.多继承.自动的强制类型转换.特别地,Java语言不使用指针,并提供了自动的废料收集,使得程序员不必为内存管理而担忧. 2.Java语言是一个面

[Java入门笔记] Java语言简介

前言 学习Java有一段时间了,但是一直缺少一个系统的思想,现在重新通过书籍中.网上的文章,视频等资料,整理与回顾Java的知识点. 言归正传,让我们先从了解Java语言开始. Java语言的由来 Java是由Sun Microsystems公司推出的Java面向对象程序设计语言(以下简称Java语言)和Java平台的总称.由James Gosling和同事们共同研发,并在1995年正式推出.Java最初被称为Oak,是1991年为消费类电子产品的嵌入式芯片而设计的.1995年更名为Java,并

java语言开发环境

[如何搭建Java开发环境] [简介] JDK的全称是 Java Development Kit,即Java标准版开发包.Sun公司提供的一套用于开发Java应用程序的开发包,提供了编译.运行Java程序所需的各种工具和资源.包括Java编译器.Java运行时环境,以及常用的Java类库等. Java运行时环境(Java Runtime Environment)JRE.是运行Java程序的必须条件. JRM包含JVM. JVM是运行Java程序的核心虚拟机. JRM包含JVM.类加载器.字节码校

java.lang.OutOfMemoryError GC overhead limit exceeded原因分析及解决方案

最近一个上线运行良好的项目出现用户无法登录或者执行某个操作时,有卡顿现象.查看了日志,出现了大量的java.lang.OutOfMemoryError: GC overhead limit exceeded错误. oracle官方给出了这个错误产生的原因和解决方法: Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded Cause: The detail message "G

java语言程序

第一章 Jave语言基础 1.1 Java语言的特点 领会Java语言的特点.关键字 1010-11.Java语言采用多种机制来保证可移植性,其中最主要的是______.虚拟机 1110-11.Java语言是一种 ____强类型语言___ 语言,它约束程序员必须遵守规定编写程序,能让编译器检测出程序中尽可能多的错误. 1.2 Java应用程序和小应用程序 识记      Java应用程序与小应用程序的特点和区别 11.一个应用程序必须在某个类中定义的方法是______.main() 1201-

2018.6.13 Java语言基础复习总结

Java语言基础与面向对象编程实践 第一章 初识Java 1.1机器语言 机器语言是指一台计算机全部的指令集合.机器语言室友0和1组成的二进制数,是一串串由0和1组成的指令序列,可将这些指令序列交给计算机执行. 1.2汇编语言 负责把汇编语言翻译成二进制的机器语言,这种翻译程序就是汇编程序. 1.3高级语言 1954年第一个完全脱离机器硬件的高级语言FORTRAN诞生 1.3.1C语言 是一种计算机程序设计语言,它既有高级语言的特点,又具有汇编语言的特点. 1.3.2 C++语言 是具有面向对象

1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、多线程(并发编程)、I/O(NIO)、Socket、JDBC、XML、反射等。[泛型]\

. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含了不同的类和程序的单一进程.线程可以被称为轻量级进程.线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源. 2. 多线程编程的好处是什么? 在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态. 3. 用户线程和守护线程有什么区别? 当我们在Java

简单的入门Android开发和Java语言基础[图]

简单的入门Android开发和Java语言基础[图]前言:去年年底到今年年初,我做过简单的智能家居,也实现过一些简单的直连和远程的智能家居.于是就将最简单的通信发布出来:智能家居简单实现-使用ESP8266简单实现和APP通讯由于这方面的资料少之又少,所以阅读量飙升的很快.也有很多人加了好友问我问题.通过问题我发现,很多都是学硬件的.或是做智能家居方面的学生,基本都不会 Android 开发的.问也有很多问我怎么学 Android 开发的.很多的人认知还是停留在学习这种软件开发技术需要去培训机构

java语言编程基础

java语言基本要素 高级语言如c++.c#.java等都有一些共同性的东西:关键字.标识符. 运算符.注释.数据类型.常量和变量.语句.函数.数组.高级语言在这些要素上大同小异. Java关键字:一些有特定含义,有专门用途的字符串(keyword).Java中关键字均为小写.如do while if int等.不需要记,用多了自然会知道. 标识符 自定义的名称.如类名.变量名.方法名等.合法标识符规则:1 不可使用关键子字 2 不能以数字开头.在java中标识符可有26个英文大小写字母.数字0