Skip to content

Allow to free direct buffers on java9 again#6568

Closed
normanmaurer wants to merge 1 commit into4.1from
cleaner9
Closed

Allow to free direct buffers on java9 again#6568
normanmaurer wants to merge 1 commit into4.1from
cleaner9

Conversation

@normanmaurer
Copy link
Copy Markdown
Member

Motivation:

Java9 adds a new method to Unsafe which allows to free direct ByteBuffer via the cleaner without the need to use an commandline arguments.

Modifications:

  • Add Cleaner interface
  • Add CleanerJava9 which will be used when using Java9+ and take care of release direct ByteBuffer
  • Let Cleaner0 implement Cleaner

Result:

Be able to free direct ByteBuffer on Java9+ again without any commandline arguments.

DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");

if (direct != null) {
freeDirectBuffer(direct);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing this makes it easier to break cycle references and its not super important anyway

@normanmaurer normanmaurer self-assigned this Mar 25, 2017
@normanmaurer normanmaurer added this to the 4.0.46.Final milestone Mar 25, 2017
@johnou
Copy link
Copy Markdown
Contributor

johnou commented Mar 25, 2017

@normanmaurer how about the non unsafe version? https://bugs.openjdk.java.net/browse/JDK-8148117

@normanmaurer
Copy link
Copy Markdown
Member Author

@johnou not sure i get your question... Can you explain ?

* For more details see <a href="https://github.com/netty/netty/issues/2604">#2604</a>.
*/
final class Cleaner0 {
final class Cleaner0 implements Cleaner {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its fine... @Scottmitch @nmittler @trustin PTAL

Copy link
Copy Markdown
Contributor

@fenik17 fenik17 Mar 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a possible deadlock such as google/guava#1977?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I found the cycle... stay tuned

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@johnou
Copy link
Copy Markdown
Contributor

johnou commented Mar 26, 2017

@normanmaurer nvm, I misread "If, at some point in the future, it is determined that sun.misc.Cleaner is in fact a critical internal API ( with static usage ),
then it can be reinstated as a subtype of jdk.internal.ref.Cleaner, providing the same public API. " as though there would already be a public API.

@normanmaurer
Copy link
Copy Markdown
Member Author

@trustin PTAL as well

@normanmaurer normanmaurer force-pushed the cleaner9 branch 2 times, most recently from cb58839 to 4fde8fa Compare March 28, 2017 08:16
try {
Class<?> bitsClass =
Class.forName("java.nio.Bits", false, PlatformDependent.getSystemClassLoader());
Class.forName("java.nio.Bits", false, getSystemClassLoader());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixed a cycle reference


if (direct != null) {
freeDirectBuffer(direct);
private static boolean explicitNoUnsafe0() {
Copy link
Copy Markdown
Member Author

@normanmaurer normanmaurer Mar 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this from PlatformDependent to remove a cycle reference.

INVOKE_CLEANER = method;
}

private static void freeDirectBuffer0(ByteBuffer buffer) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this method necessary? seems like it is only used in a single place

}

static void freeDirectBuffer(ByteBuffer buffer) {
private static void freeDirectBuffer0(ByteBuffer buffer) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove this method, and just move the code into freeDirectBuffer. We invoke the cleaner above clean.invoke(cleaner); so we may be double freeing this buffer

}

if (!hasUnsafe() && !isAndroid() && !IS_EXPLICIT_NO_UNSAFE) {
if (!hasUnsafe() && !isAndroid() && !PlatformDependent0.isExplicitNoUnsafe()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isExplicitNoUnsafe should be reflected in hasUnsafe ... can we just remove the !PlatformDependent0.isExplicitNoUnsafe() part?

if (hasUnsafe() && !isAndroid()) {
// only direct to method if we are not running on android.
// See https://github.com/netty/netty/issues/2604
CLEANER = javaVersion() >= 9 ? new CleanerJava9() : new Cleaner0();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider renaming Cleaner0 to CleanerJava6.

@normanmaurer
Copy link
Copy Markdown
Member Author

@Scottmitch addressed all of it... Good to merge ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the conditional below checks if error == null and then says "cleaner is available". However this may not be the case if we have no unsafe. We should adjust the logic below or set the error = <exception indicating unsafe isn't available>.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove this assert ... we will very likely NPE via a JVM trap below anyways.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we log a debug statement, or is this expected to happen frequently?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would argue this should never happen as we tried it before in the static block. Maybe best to throw an Error . WDYT ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yah if we never expect it to happen lets re-throw it. What about PlatformDependent.throwException(t);?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also log something similar to the Java6 class if the cleaner is not supported.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: instead of checking this condition every time we could have PlatformDependent detect if the cleaner is properly initialized, and if not just use the NOOP cleaner. We should do the same thing for the java6 class:

class CleanerJava9 {

static {
..
}

static boolean isInitialized() {
  return INVOKE_CLEANER != null;
}
..
}

class PlatformDependent {
..
if (java9) {
  CLEANER = CleanerJava9.isInitialized() ? new CleanerJava9() : NOOP;
} else {
  CLEANER = CleanerJava6.isInitialized() ? new CleanerJava6() : NOOP;
}
...
}

@normanmaurer normanmaurer force-pushed the cleaner9 branch 2 times, most recently from 580c10b to 8de8345 Compare March 29, 2017 20:23
Copy link
Copy Markdown
Member

@Scottmitch Scottmitch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few comments then lgtm


// free buffer if possible
freeDirectBuffer(direct);
public static boolean isSupported() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package private?

INVOKE_CLEANER = method;
}

public static boolean isSupported() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package private?

private static final Method INVOKE_CLEANER;

static {
Method method;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final?


static {
Method method;
Throwable error;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final?

Motivation:

Java9 adds a new method to Unsafe which allows to free direct ByteBuffer via the cleaner without the need to use an commandline arguments.

Modifications:

- Add Cleaner interface
- Add CleanerJava9 which will be used when using Java9+ and take care of release direct ByteBuffer
- Let Cleaner0 implement Cleaner

Result:

Be able to free direct ByteBuffer on Java9+ again without any commandline arguments.
@normanmaurer
Copy link
Copy Markdown
Member Author

Cherry-picked into 4.1 (7b6119a) and 4.0 (295c24e)

@normanmaurer normanmaurer deleted the cleaner9 branch April 17, 2017 17:42
logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED);
}

if (!hasUnsafe() && !isAndroid() && !IS_EXPLICIT_NO_UNSAFE) {
Copy link
Copy Markdown
Contributor

@jasontedor jasontedor May 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this removed? It means that logging for this shows up even if no unsafe was explicitly set which was the point of adding this in the first place. It means that with Netty 4.1.10.Final our users are back to seeing a message that they don't understand that makes them think their system is broken. Unless there is a good reason that this is a removed (I'm sorry if I'm missing it), I would like to open a PR to add it back.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relates #5624

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it seems to me that this change was not needed for the stated purpose of this change (to allow freeing direct buffers on JDK 9)? Again, maybe I'm missing something and if so I'm sorry.

Adding unrelated changes to PRs makes it hard to follow what is going on. I follow this repository, if I see a change titled "Allow to free direct buffers on java9 again" I think I know what it's about and I skip reading the code change. If an unrelated code change is slipped in, how can I as a follower know this without reading every code change which I think is an unreasonable assumption?

Had this been in a separate PR I would have seen it and would have responded then to please not remove it.

@normanmaurer
Copy link
Copy Markdown
Member Author

normanmaurer commented May 3, 2017 via email

@jasontedor
Copy link
Copy Markdown
Contributor

Thank you for understanding @normanmaurer! I will open a PR soon.

Can you please do a PR and also add a comment in the code why it should never be removed again.

I will do this! 😄

@jasontedor
Copy link
Copy Markdown
Contributor

I opened #6696.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants