英文:
Why some garbage collected & OOP programming language doesn't have a destructor?
问题
考虑下面的情况
当我们在类内部使用C API来创建一些使用malloc在堆中分配的数据时(例如 Object* create_obj()
),并且我们必须在类生命周期结束时调用特定的方法(void free_obj()
)来手动释放内存。
当一种语言拥有析构函数时,我们可以将 free_obj
轻松地放在类析构函数中,这样用户就不必手动调用 free_obj
,并且可以等到类被垃圾收集。
我的问题
-
为什么一些带有垃圾收集和面向对象编程功能的语言(例如Java [Java已经不再推荐使用它的
finalize
] 和Ruby)没有析构函数? -
当与上述情况类似的低级API进行交互时,析构函数不是必需的吗?如果不是必需的,那么解决下面问题的最佳实践是什么?
英文:
Consider the case below
When we are using a C API's inside a class to create some data that are allocated in heap using malloc (e.g Object* create_obj()
), and we have to call a certain method (void free_obj()
) in the end of the class lifetime to free the memory manually.
When a language has a destructor, we can easily put the free_obj
in the class destructor so the user does not have to call the free_obj
manually and wait until the class get garbage collected.
My question
-
Why some garbage collected & OOP programming language (Java [Java has been deprecating it's
finalize
] and Ruby) doesn't have a destructor? -
Isn't a destructor is necessary when you're interfacing a low level API like for the case above? If it's not necessary, what is the best practice to solve the problem below?
答案1
得分: 3
像Java和Ruby这样的语言拥有finalizers,但没有destructors。主要原因是确定性销毁会以语言设计者不希望实现的方式限制实现。
现代高性能垃圾收集器使用的许多性能技巧在确定性finalization下是不可能实现的。甚至Ruby和Java都不能保证对象会被完全回收。对于Ruby或Java的实现来说,即使对象不可达,从法律上讲,永远不回收对象都是合法的。
甚至CPython这个只有非常简单垃圾回收器的语言,也不能保证确定性finalization。它只能保证非循环对象图的确定性finalization。Python社区已经非常明确地表示这是CPython的私有内部实现细节,不是Python语言语义的一部分,这意味着其他实现(例如PyPy,IronPython,Jython)不必实现它,因此可以自由地实现更好的垃圾收集器。
英文:
Languages like Java and Ruby have finalizers, but not destructors. The main reason is that deterministic destruction constrains the implementation in a way that the language designers did not want to do.
Many of the performance tricks that modern high-performance garbage collectors employ would not be possible with deterministic finalization. Ruby and Java do not even guarantee that an object will be collected at all. It is perfectly legal for a Ruby or Java implementation to never collect an object even if it is unreachable.
Even CPython, which has a very simple garbage collector cannot guarantee deterministic finalization. It only guarantees deterministic finalization for non-cyclic object graphs. And the Python community has made it very clear that this is a private internal implementation detail of CPython and not part of Python language semantics, meaning that other implementations (e.g. PyPy, IronPython, Jython) do not have to implement it and are thus free to implement much better garbage collectors.
答案2
得分: 1
析构函数在基于分配的语言中是必需的,但在像Ruby这样的GC(垃圾回收)语言中是可选的。析构函数模式不应与垃圾回收混淆,正如你所说,它们代表了将对象的生命周期与作用域匹配。
对象存在一段时间,然后所使用的内存部分被标记为将来对象可用的内存。Ruby提供了两组内存:malloc堆
和Ruby对象堆
。malloc堆
只有在Ruby在gc结束时未使用内存时才会释放回操作系统。后者(malloc堆
的子集)是大多数Ruby对象所在的地方。Ruby垃圾回收器将重点放在这里,并经常进行清理,这意味着析构函数在很大程度上是不必要的。并非每个对象都会被回收,但是像Ruby这样的语言并不能保证每个对象都会被回收。
在Ruby中,变量引用一个对象,这意味着对象存储在某个地方,而变量只保存对象的ID。如果我们在已被另一个变量回收或析构的对象上调用析构函数,它将返回nil
,但可能是相同的对象ID,这可能会在运行时引发问题。
Ruby的define_finalizer
不是常见做法,鼓励开发者不要使用它。该方法无法引用正在释放的对象,因为回调是在对象被释放后执行的,因此不能保证会调用它。如果finalizer
处理器保持对self
的引用,这将导致无法对对象进行垃圾回收,这意味着它永远不会被回收。
英文:
Destructors are necessary in allocation based languages, but optional in GC languages like Ruby. Destructor patterns are not to be confused with garbage collection and are, as you said, representative of matching an object lifespan to a scope.
Objects live for a while and then the section of memory said object consumes is marked as available for future objects. Ruby offers two sets of memory: malloc heap
and Ruby object heap
. malloc heap
does not release back to the os unless the memory is unused by Ruby at the end of gc. The latter (a subset of malloc heap
) is where most Ruby objects live. The Ruby garbage collector directs its focus here and cleans up often, meaning destructors are, for the most part, unnecessary. Not every object will be collected but languages like Ruby do not guarantee that.
In Ruby variables reference an Object which means an Object is stored somewhere and variables only hold the Object id. If we called a destructor on such an object that has been collected or destructed by another variable it would return nil
but possibly the same object id, which could cause issues at run time.
Ruby's define_finalizer
is not common practice and developers are discouraged from using it. The method cannot refer to the object it is freeing as the callback is executed after the object is freed, so there's no guarantee it will be called. If a finalizer
proc holds reference to self
which would make it impossible for the object to be garbage collected, meaning it will never be collected.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论