[External] : Re: Virtual thread memory leak because TrackingRootContainer keeps threads

Alex Otenko oleksandr.otenko at gmail.com
Sun Aug 4 19:37:25 UTC 2024


I guess that's the learning we can take from this. That when you encounter
non-termination, the program may behave in ways that you may find odd, even
though the language specification allows it.

On Sun, 4 Aug 2024, 18:15 robert engels, <robaho at icloud.com> wrote:

> Actually, an even simpler example would be try-with-resources. With this
> proposed behavior you could see a resource enqueued as collected without
> the close() ever being performed. This is an impossibility according to the
> specification.
>
> On Aug 4, 2024, at 11:36 AM, robert engels <robaho at icloud.com> wrote:
>
> 
> Put it another way for a reference to be enqueued it would require that
> the thread executed the finally block - which is not happening in this
> case. So you are observing an outcome that shouldn’t be possible given the
> semantics of try/finally and reachability.
>
> On Aug 4, 2024, at 11:33 AM, robert engels <robaho at icloud.com> wrote:
>
> 
> I’m sorry that is not correct. There is still a reference to the object.
> The statements after the blocked statement clear the reference. There is
> nothing in the spec that states an infinitely blocked thread means all of
> the threads references are no longer valid and can be considered cleared.
>
> This is the exact reason Thread.stop() was deprecated as I shared.
>
> You simply can’t do this and create auditable systems - you are messing
> with program order and reachability guarantees.
>
> On Aug 4, 2024, at 11:19 AM, Alex Otenko <oleksandr.otenko at gmail.com>
> wrote:
>
> 
> I did. The statement that you copied means that no statement from the
> thread's code can be executed. Finalizer isn't part of that code.
>
> The finalizer section says that finalizer can be executed only after we
> can tell that no more actions to access the object can be performed. See,
> no notion of garbage, just that no further action can access it. Which is
> the case in the case of a hang.
>
> On Sun, 4 Aug 2024, 17:08 robert engels, <robaho at icloud.com> wrote:
>
>> Did you read what you copied? It proves my point:
>>
>> “ In such cases, the actions generated by the blocked thread must
>> consist of all actions generated by that thread up to and including the
>> action that caused the thread to be blocked, and no actions that would be
>> generated by the thread after that action.”
>>
>> Given that, the objects that were held can not be observed as being
>> released - I.e. cannot placed in reference queues.
>>
>> So thank you, you provided the definitive answer that this ephemeral vt
>> handling is not permitted.
>>
>>
>> On Aug 4, 2024, at 7:17 AM, Alex Otenko <oleksandr.otenko at gmail.com>
>> wrote:
>>
>> 
>>
>> > implementation
>>
>> You said it :)
>>
>> OK
>>
>> https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.9
>> - non-termination
>>
>> https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.6.2
>> - finalization
>>
>> It all checks out, I think.
>>
>> On Sun, 4 Aug 2024, 12:50 robert engels, <robaho at icloud.com> wrote:
>>
>>> I can understand that reasoning but the problem is that it doesn’t align
>>> with current openjdk implementation. The observed behavior is the exact
>>> reason stop was deprecated with the exact same vulnerabilities.
>>>
>>> On Aug 4, 2024, at 6:14 AM, Alex Otenko <oleksandr.otenko at gmail.com>
>>> wrote:
>>>
>>> 
>>>
>>> I think the anguish may be coming from the GC / GC roots / finalizer
>>> behaviour being undefined. We'd all prefer things to be deterministic, but
>>> this one thing isn't.
>>>
>>> So the solution is to not try and make a certain flavour of behaviour
>>> guaranteed, but to revisit our assumptions about the expected code
>>> behaviours. That some of our proofs of correctness may be based on
>>> implementation detail and not on real guarantees.
>>>
>>> On Sun, 4 Aug 2024, 12:05 robert engels, <robaho at icloud.com> wrote:
>>>
>>>> Avoiding was not the right word. Apologies. I only mean that I would
>>>> like to understand the difference. I know it is at a know point vs
>>>> arbitrary, but stop()could have been changed to only be at a safe point.
>>>> Stop has an issue with possibly being in native code as well - but the
>>>> deprecation justification doesn’t state any of this - only the stack
>>>> unwinding which is the same issue that this capability in virtual thread
>>>> has.
>>>>
>>>> > On Aug 3, 2024, at 7:00 PM, robert engels <robaho at icloud.com> wrote:
>>>> >
>>>> > Can you help me by stating why this is different than the thread
>>>> stop?
>>>> >
>>>> > Because I think you could implement this by throwing a ThreadDeath -
>>>> or as I suggested just reclaiming the memory. It feels like you are
>>>> avoiding this issue for some reason.
>>>> >
>>>> >> On Aug 3, 2024, at 6:08 PM, Ron Pressler <ron.pressler at oracle.com>
>>>> wrote:
>>>> >>
>>>> >> 
>>>> >>
>>>> >>>> On 2 Aug 2024, at 15:58, robert engels <robaho at icloud.com> wrote:
>>>> >>>
>>>> >>> Sorry to keep this thread going but in reviewing my test results I
>>>> think this capability is now dangerous, as I related to the Thread.stop().
>>>> It is going to be the same issue.
>>>> >>>
>>>> >>> A lot of native resource backed objects have Java shadows with
>>>> finalizers / reference queues. If the referenced are released without
>>>> traveling up the stack the program/system can be in an inconsistent state.
>>>> >>>
>>>> >>> Take for instance a file system lock file that is removed on
>>>> finalization. This would be removed allowing other processing to continue
>>>> even though the locking process never completed nor rolled back.
>>>> >>>
>>>> >>> Maybe you could argue it is a bad design to do this but I think the
>>>> pattern with Cleaners, etc is fairly pervasive.
>>>> >>>
>>>> >>> By releasing the objects program order guarantees are broken - I
>>>> don’t think it is valid to do this - especially concerning try/finally.
>>>> >>>
>>>> >>> As I also said you could maybe be very tricky and just release the
>>>> memory but that isn’t what is happening here. It is collecting the objects
>>>> which allows side effects through finalization and reference queues.
>>>> >>
>>>> >>
>>>> >> You are absolutely right that finalizers (and reference
>>>> queues/cleaners in general) are a dangerous feature that should be avoided,
>>>> but that’s the case regardless of thread collection, and even in the case
>>>> of thread stacks, remember that references on the stack may be collected
>>>> even if the thread is reachable, which is why ReachabilityFence exists. It
>>>> is simply *not* the case, neither in theory nor in practice, that only
>>>> references that have been popped off the stack are collected. That is *not*
>>>> a guarantee, and if it's your assumption, then you’re in for some surprises
>>>> and shouldn’t use finalizers/cleaners. In fact, the general advice is not
>>>> to use these mechanisms at all. They don’t work as most people might assume
>>>> they do.
>>>> >>
>>>> >> Having said all that, collecting a thread whose references are
>>>> already allowed to be collected is valid.
>>>>
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20240804/e1cce2ad/attachment-0001.htm>


More information about the loom-dev mailing list