Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8227745: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents #119

Closed
wants to merge 29 commits into from

Conversation

reinrich
Copy link
Member

@reinrich reinrich commented Sep 10, 2020

Hi,

this is the continuation of the review of the implementation for:

https://bugs.openjdk.java.net/browse/JDK-8227745
https://bugs.openjdk.java.net/browse/JDK-8233915

It allows for JIT optimizations based on escape analysis even if JVMTI agents acquire capabilities to access references to objects that are subject to such optimizations, e.g. scalar replacement.
The implementation reverts such optimizations just before access very much as when switching from JIT compiled execution to the interpreter, aka "deoptimization".

Webrev.8 was the last one before before the transition to Git/Github:

http://cr.openjdk.java.net/~rrich/webrevs/8227745/webrev.8/

Thanks, Richard.


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed

Testing

Linux x64 Windows x64 macOS x64
Build ✔️ (5/5 passed) ✔️ (2/2 passed) ✔️ (2/2 passed)
Test (tier1) ✔️ (9/9 passed) ✔️ (9/9 passed) ✔️ (9/9 passed)

Issues

  • JDK-8227745: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents
  • JDK-8233915: JVMTI FollowReferences: Java Heap Leak not found because of C2 Scalar Replacement

Reviewers

Download

$ git fetch https://git.openjdk.java.net/jdk pull/119/head:pull/119
$ git checkout pull/119

@bridgekeeper
Copy link

bridgekeeper bot commented Sep 10, 2020

👋 Welcome back rrich! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@reinrich
Copy link
Member Author

/issue add JDK-8233915

@openjdk
Copy link

openjdk bot commented Sep 10, 2020

@reinrich
Adding additional issue to issue list: 8233915: JVMTI FollowReferences: Java Heap Leak not found because of C2 Scalar Replacement.

@reinrich reinrich marked this pull request as ready for review September 10, 2020 21:03
@openjdk openjdk bot added the rfr Pull request is ready for review label Sep 10, 2020
@reinrich
Copy link
Member Author

/label add hotspot

@openjdk openjdk bot added the hotspot hotspot-dev@openjdk.org label Sep 10, 2020
@openjdk
Copy link

openjdk bot commented Sep 10, 2020

@reinrich
The hotspot label was successfully added.

@mlbridge
Copy link

mlbridge bot commented Sep 10, 2020

Webrevs

Copy link
Contributor

@TheRealMDoerr TheRealMDoerr left a comment

Choose a reason for hiding this comment

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

Hi Richard,
I had reviewed an older webrev. Your newest improvements look good.

@openjdk
Copy link

openjdk bot commented Sep 11, 2020

@reinrich This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8227745: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents
8233915: JVMTI FollowReferences: Java Heap Leak not found because of C2 Scalar Replacement

Reviewed-by: mdoerr, goetz, sspitsyn, kvn

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 28 new commits pushed to the master branch:

  • 294e070: 8254998: C2: assert(!n->as_Loop()->is_transformed_long_loop()) failure with -XX:StressLongCountedLoop=1
  • 3ee0380: 8234393: [macos] printing ignores printer tray
  • 3f9c8a3: 8254966: Remove unused code from Matcher
  • 21e67e5: 8225790: Two NestedDialogs tests fail on Ubuntu
  • 017d151: 8254842: [JVMCI] copy thread name when attaching libgraal thread to HotSpot
  • 5d1397f: 8254282: Add Linux x86_32 builds to submit workflow
  • 7ba6a6b: 8251158: Implementation of JEP 387: Elastic Metaspace
  • 5fedfa7: 8251271: C2: Compile::_for_igvn list is corrupted after RenumberLiveNodes
  • 98ec4a6: 8254805: compiler/debug/TestStressCM.java is still failing
  • 355f44d: 8254994: [x86] C1 StubAssembler::call_RT, "call_offset might not be initialized"
  • ... and 18 more: https://git.openjdk.java.net/jdk/compare/1da28de82f042440e84c4ce6751dd00ff2ee2ed5...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Sep 11, 2020
@reinrich
Copy link
Member Author

Hi Richard,
I had reviewed an older webrev. Your newest improvements look good.

Thanks Martin!

@reinrich
Copy link
Member Author

@reinrich This change now passes all automated pre-integration checks. In addition to the automated checks, the change must also fulfill all project specific requirements

After integration, the commit message will be:

8227745: 8233915: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents
8233915: JVMTI FollowReferences: Java Heap Leak not found because of C2 Scalar Replacement

Reviewed-by: mdoerr

The commit message looks odd to me. Should I remove ", 8233915" from the title of this request? I used the old scheme "JBS-Id[, JBS-Id]*" and this might be wrong.

@edvbld
Copy link
Member

edvbld commented Sep 11, 2020

The commit message is correct according to JEP 357

@edvbld
Copy link
Member

edvbld commented Sep 11, 2020

Sorry, now I see. Yes, please remove , 8233915 from the title!

@openjdk
Copy link

openjdk bot commented Sep 11, 2020

@reinrich The following labels will be automatically applied to this pull request: core-libs serviceability.

When this pull request is ready to be reviewed, an RFR email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label (add|remove) "label" command.

@openjdk openjdk bot added serviceability serviceability-dev@openjdk.org core-libs core-libs-dev@openjdk.org labels Sep 11, 2020
@reinrich reinrich changed the title 8227745, 8233915: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents 8227745: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents Sep 11, 2020
@reinrich
Copy link
Member Author

Sorry, now I see. Yes, please remove , 8233915 from the title!

Thanks for helping. The commit message does look better now.

Copy link
Member

@dholmes-ora dholmes-ora left a comment

Choose a reason for hiding this comment

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

I can't review this in-depth as I am not familiar with too much of the code. I have looked at some of the key threading related parts and have made a number of comments.

src/hotspot/share/compiler/compileBroker.cpp Show resolved Hide resolved
src/hotspot/share/compiler/compileBroker.cpp Outdated Show resolved Hide resolved
src/hotspot/share/compiler/compileBroker.cpp Outdated Show resolved Hide resolved
src/hotspot/share/opto/macro.cpp Show resolved Hide resolved
src/hotspot/share/prims/whitebox.cpp Outdated Show resolved Hide resolved
src/hotspot/share/runtime/thread.cpp Show resolved Hide resolved
src/hotspot/share/runtime/thread.cpp Outdated Show resolved Hide resolved
src/hotspot/share/runtime/thread.cpp Show resolved Hide resolved
src/hotspot/share/runtime/thread.cpp Outdated Show resolved Hide resolved
@mlbridge
Copy link

mlbridge bot commented Sep 15, 2020

Mailing list message from David Holmes on hotspot-dev:

On 14/09/2020 6:18 pm, Richard Reingruber wrote:

On Mon, 14 Sep 2020 04:57:21 GMT, David Holmes <dholmes at openjdk.org> wrote:

Hi,

this is the continuation of the review of the implementation for:

https://bugs.openjdk.java.net/browse/JDK-8227745
https://bugs.openjdk.java.net/browse/JDK-8233915

It allows for JIT optimizations based on escape analysis even if JVMTI agents acquire capabilities to access references
to objects that are subject to such optimizations, e.g. scalar replacement. The implementation reverts such
optimizations just before access very much as when switching from JIT compiled execution to the interpreter, aka
"deoptimization". Webrev.8 was the last one before before the transition to Git/Github:

http://cr.openjdk.java.net/~rrich/webrevs/8227745/webrev.8/

Thanks, Richard.

src/hotspot/share/compiler/compileBroker.cpp line 831:

829: MonitorLocker ml(dt, EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
830: static int single_thread_count = 0;
831: enter_single_loop = single_thread_count++ < DeoptimizeObjectsALotThreadCountSingle;

The update to `single_thread_count` is not atomic.

I think it is atomic because it is never accessed without holding EscapeBarrier_lock

Doh! My bad.

David

@GoeLin
Copy link
Member

GoeLin commented Sep 17, 2020

Reviewed in the good old hg times :)
See also http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/2020-August/041453.html

@sspitsyn
Copy link
Contributor

sspitsyn commented Sep 22, 2020

Hi Richard,

Could you consider to place the classes EscapeBarrier and JvmtiDeferredUpdates into theyr own .hpp/.cpp files? The class JvmtiDeferredUpdates would be better to put into the folder 'prims' then.

src/hotspot/share/opto/macro.cpp:

@@ -1091,11 +1091,11 @@
 bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) {
   // Don't do scalar replacement if the frame can be popped by JVMTI:
   // if reallocation fails during deoptimization we'll pop all
   // interpreter frames for this compiled frame and that won't play
   // nice with JVMTI popframe.
-  if (!EliminateAllocations || JvmtiExport::can_pop_frame() || !alloc->_is_non_escaping) {
+  if (!EliminateAllocations || !alloc->_is_non_escaping) {
     return false;
   }

I wonder if the comment is still correct after you removed the check for JvmtiExport::can_pop_frame().

src/hotspot/share/runtime/deoptimization.hpp:

+  EscapeBarrier(JavaThread* calling_thread, JavaThread* deoptee_thread, bool barrier_active)
+    : _calling_thread(calling_thread), _deoptee_thread(deoptee_thread),
+      _barrier_active(barrier_active && (JVMCI_ONLY(UseJVMCICompiler) NOT_JVMCI(false)
+                      COMPILER2_PRESENT(|| DoEscapeAnalysis)))
. . . . . . . . .
+
+  // Revert ea based optimizations for all java threads
+  EscapeBarrier(JavaThread* calling_thread, bool barrier_active)
+    : _calling_thread(calling_thread), _deoptee_thread(NULL),

Nit: would better to make the parameter deoptee_thread to be the 3rd to better mach the seconf constructor.

+  bool all_threads()    const { return _deoptee_thread == NULL; }            // Should revert optimizations for all threads.
+  bool self_deopt()     const { return _calling_thread == _deoptee_thread; } // Current thread deoptimizes its own objects.
+  bool barrier_active() const { return _barrier_active; }                    // Inactive barriers are created if no local objects can escape.

I'd suggest to put comments in a line before function definitions as it is done for other declarations/definitions.

@sspitsyn
Copy link
Contributor

sspitsyn commented Sep 22, 2020

src/hotspot/share/runtime/deoptimization.cpp:

 @@ -349,12 +408,12 @@
 
   // Now that the vframeArray has been created if we have any deferred local writes
   // added by jvmti then we can free up that structure as the data is now in the
   // vframeArray
 
-  if (thread->deferred_locals() != NULL) {
-    GrowableArray<jvmtiDeferredLocalVariableSet*>* list = thread->deferred_locals();
+  if (JvmtiDeferredUpdates::deferred_locals(thread) != NULL) {
+    GrowableArray<jvmtiDeferredLocalVariableSet*>* list = JvmtiDeferredUpdates::deferred_locals(thread);
     int i = 0;
     do {
       // Because of inlining we could have multiple vframes for a single frame
       // and several of the vframes could have deferred writes. Find them all.
       if (list->at(i)->id() == array->original().id()) {

@@ -365,13 +424,14 @@
       } else {
         i++;
       }
     } while ( i < list->length() );
     if (list->length() == 0) {
-      thread->set_deferred_locals(NULL);
-      // free the list and elements back to C heap.
-      delete list;
+      JvmtiDeferredUpdates* updates = thread->deferred_updates();
+      thread->set_deferred_updates(NULL);
+      // free deferred updates.
+      delete updates;
     }

It is not clear why the 'list' is not deleted anymore. If it is intentional then could you, please, add a comment with an explanation?

If you are okay to separate the EscapeBarrier class into its own hpp/cpp files then the class EscapeBarrierSuspendHandshake is better to be colocated with it.

The below functions EscapeBarrier::sync_and_suspend_one() and do_thread() make a call to the set_obj_deopt_flag() which seems to be a duplication. At least, it is not clear why this duplication exist and so, needs to be explained in a comment.

+void EscapeBarrier::sync_and_suspend_one() {
+  assert(_calling_thread != NULL, "calling thread must not be NULL");
+  assert(_deoptee_thread != NULL, "deoptee thread must not be NULL");
+  assert(barrier_active(), "should not call");
+
+  // Sync with other threads that might be doing deoptimizations
+  {
+    // Need to switch to _thread_blocked for the wait() call
+    ThreadBlockInVM tbivm(_calling_thread);
+    MonitorLocker ml(_calling_thread, EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
+    while (_self_deoptimization_in_progress || _deoptee_thread->is_obj_deopt_suspend()) {
+      ml.wait();
+    }
+
+    if (self_deopt()) {
+      _self_deoptimization_in_progress = true;
+      return;
+    }
+
+    // set suspend flag for target thread
+    _deoptee_thread->set_obj_deopt_flag();
+  }
+
+  // suspend target thread
+  EscapeBarrierSuspendHandshake sh(NULL, "EscapeBarrierSuspendOne");
+  Handshake::execute_direct(&sh, _deoptee_thread);
+  assert(!_deoptee_thread->has_last_Java_frame() || _deoptee_thread->frame_anchor()->walkable(),
+         "stack should be walkable now");
+}
. . . . .
+class EscapeBarrierSuspendHandshake : public HandshakeClosure {
+  JavaThread* _excluded_thread;
+ public:
+  EscapeBarrierSuspendHandshake(JavaThread* excluded_thread, const char* name) :
+    HandshakeClosure(name),
+    _excluded_thread(excluded_thread) {}
+  void do_thread(Thread* th) {
+    if (th->is_Java_thread() && !th->is_hidden_from_external_view() && (th != _excluded_thread)) {
+      th->set_obj_deopt_flag();
+    }
+  }
+};

/src/hotspot/share/prims/jvmtiImpl.cpp:

421 // Constructor for non-object getter
 422 VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type)
 423   : _thread(thread)
 424   , _calling_thread(NULL)
 425   , _depth(depth)
 426   , _index(index)
 427   , _type(type)
 428   , _jvf(NULL)
 429   , _set(false)
 430   , _eb(NULL, NULL, type == T_OBJECT)
 431   , _result(JVMTI_ERROR_NONE)
 432 {
 433 }
 434 
 435 // Constructor for object or non-object setter
 436 VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, jvalue value)
 437   : _thread(thread)
 438   , _calling_thread(NULL)
 439   , _depth(depth)
 440   , _index(index)
 441   , _type(type)
 442   , _value(value)
 443   , _jvf(NULL)
 444   , _set(true)
 445   , _eb(JavaThread::current(), thread, type == T_OBJECT)
 446   , _result(JVMTI_ERROR_NONE)
 447 {
 448 }
 449 
 450 // Constructor for object getter
 451 VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, int index)
 452   : _thread(thread)
 453   , _calling_thread(calling_thread)
 454   , _depth(depth)
 455   , _index(index)
 456   , _type(T_OBJECT)
 457   , _jvf(NULL)
 458   , _set(false)
 459   , _eb(calling_thread, thread, true)
 460   , _result(JVMTI_ERROR_NONE)
 461 {
 462 }

I think, false has to be passed to the constructors of non-object getters instead of expression:
"type == T_OBJECT".
The type can not be T_OBJECT for non-object getters.
Q: Is an EscapeBarrier useful if false is passed as the barrier_active parameter?

EscapeBarrier::sync_and_suspend_all():

  - Set suspend flags before handshake because then the setting will be visible
    after leaving the _thread_blocked state in JavaThread::wait_for_object_deoptimization()

JavaThread::wait_for_object_deoptimization()

  - Reuse SpinYield instead of new custom spinning code.

  - Do safepoint check after the loop. This is possible because the
    set_obj_deopt_flag is done before the handshake.

  - Don't set_suspend_equivalent() anymore for more simplicity. It's just an
    optimization (that won't pay off here).

Added/updated source code comments.

Additional smaller enhancements.
Copy link
Contributor

@vnkozlov vnkozlov left a comment

Choose a reason for hiding this comment

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

Compiler changes seems fine.

Copy link
Contributor

@vnkozlov vnkozlov left a comment

Choose a reason for hiding this comment

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

I tried to run testing with latest changes and latest JDK and build failed:
src/hotspot/share/runtime/escapeBarrier.cpp:310:35: error: no matching function for call to 'StackFrameStream::StackFrameStream(JavaThread*&)'
310 | StackFrameStream fst(deoptee);

@reinrich
Copy link
Member Author

Compiler changes seems fine.

Thank you for looking again at this.

@reinrich
Copy link
Member Author

I tried to run testing with latest changes and latest JDK and build failed:
src/hotspot/share/runtime/escapeBarrier.cpp:310:35: error: no matching function for call to 'StackFrameStream::StackFrameStream(JavaThread*&)'
310 | StackFrameStream fst(deoptee);

I noticed this too. I wanted to test with ZGC before pushing the small
fix. Unfortunately I get

#  Internal Error (/priv/d038402/git/reinrich/jdk_ea_new/src/hotspot/share/runtime/stackWatermark.inline.hpp:67), pid=90890, tid=90912
#  assert(processing_started()) failed: Processing should already have started

[...]

Current thread (0x00007f749c25b1c0):  JavaThread "JDWP Transport Listener: dt_socket" daemon [_thread_in_vm, id=90912, stack(0x00007f7474c9f000,0x00007f7474da0000)] _threads_hazard_ptr=0x00007f749c2b00c0, _nested_threads_hazard_ptr_cnt=0

Stack: [0x00007f7474c9f000,0x00007f7474da0000],  sp=0x00007f7474d9c240,  free space=1012k
Native frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x15b3255]  StackWatermarkSet::on_iteration(JavaThread*, frame const&)+0xa5
V  [libjvm.so+0xa1024f]  frame::sender(RegisterMap*) const+0x13f
V  [libjvm.so+0xa048f8]  frame::real_sender(RegisterMap*) const+0x18
V  [libjvm.so+0x176261b]  vframe::sender() const+0xeb
V  [libjvm.so+0x16cd56b]  JavaThread::last_java_vframe(RegisterMap*)+0x5b
V  [libjvm.so+0xfa7a56]  JvmtiEnvBase::vframeFor(JavaThread*, int)+0x46
V  [libjvm.so+0xfab8e5]  JvmtiEnvBase::check_top_frame(JavaThread*, JavaThread*, jvalue, TosState, Handle*)+0x1f5
V  [libjvm.so+0xfac13e]  JvmtiEnvBase::force_early_return(JavaThread*, jvalue, TosState)+0x15e
V  [libjvm.so+0xf36fa8]  jvmti_ForceEarlyReturnLong+0x258
C  [libjdwp.so+0xa8b3]  forceEarlyReturn+0x293
C  [libjdwp.so+0x12945]  debugLoop_run+0x1f5
C  [libjdwp.so+0x25bb3]  attachThread+0x33
V  [libjvm.so+0xfcf524]  JvmtiAgentThread::call_start_function()+0x1d4
V  [libjvm.so+0x16cc8f7]  JavaThread::thread_main_inner()+0x247
V  [libjvm.so+0x16d1ce8]  Thread::call_run()+0xf8
V  [libjvm.so+0x12dd75e]  thread_native_entry(Thread*)+0x10e

In the test case
EAForceEarlyReturnOfInlinedMethodWithScalarReplacedObjectsReallocFailure of the
new test jdk/com/sun/jdi/EATests.java

So far I do not have an indication that the failure is caused by this change but
when I run the test with -XX:-DoEscapeAnalysis then the test succeeds.

I need to look more into it. Wish I was a ZGC expert :)

Anyway I pushed the build fix. Tests succeed with default GC.

@reinrich
Copy link
Member Author

The crash described above happens after JDK-8253180 (b9873e1) when executing EATests.java with ZGC:

make run-test TEST=test/jdk/com/sun/jdi/EATests.java JTREG=VM_OPTIONS=-XX:+UseZGC

My understanding of JDK-8253180 (and ZGC) is rather vague. To me it looks as if stackwalks outside of a safepoint/handshake on suspended threads are currently not supported. It would be my understanding that StackWatermarkSet::start_processing() needs to be called before walking the stack of a thread. Currently this is only done in preparation of a safepoint or handshake.

JvmtiEnvBase::check_top_frame() walks the stack of a suspended thread without safepoint/handshake. This triggers the crash in my opinion. When StackWatermarkSet::start_processing() is called before the test succeeds.

I will ask Erik Österlund about this.

@reinrich
Copy link
Member Author

The issues with ZGC concurrent thread stack processing will be resolved with #627

Copy link
Contributor

@vnkozlov vnkozlov left a comment

Choose a reason for hiding this comment

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

Good.

@reinrich
Copy link
Member Author

Good.

Thanks for the review, Vladimir (@vnkozlov)!
I'm still (stress) testing adaptations to lazy/concurrent thread stack processing for ZGC.
--Richard.

…heck from ~ThreadInVMForHandshake()

With JDK-8254263 the special_runtime_exit_condition() check was removed from
~ThreadInVMForHandshake() because now a thread never becomes unsafe when
processing its own handshakes. EscapeBarrier uses handshakes to sync with the
target thread for object deoptimization so we add a check for object
deoptimization to ThreadSafepointState::handle_polling_page_exception().

In JavaThread::wait_for_object_deoptimization() we must check
is_obj_deopt_suspend() again after handshake/safepoint processing because a
handshake for obj. deopt suspend could have been processed.
@reinrich
Copy link
Member Author

Thanks once more @TheRealMDoerr, @GoeLin, @dholmes-ora, @sspitsyn, @vnkozlov, @robehn, @pchilano for feedback and reviews.

@AlanBateman
Copy link
Contributor

/label remove core-libs

@openjdk openjdk bot removed the core-libs core-libs-dev@openjdk.org label Oct 20, 2020
@openjdk
Copy link

openjdk bot commented Oct 20, 2020

@AlanBateman
The core-libs label was successfully removed.

@reinrich
Copy link
Member Author

/integrate

@openjdk openjdk bot closed this Oct 20, 2020
@openjdk openjdk bot added integrated Pull request has been integrated and removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Oct 20, 2020
@openjdk
Copy link

openjdk bot commented Oct 20, 2020

@reinrich Since your change was applied there have been 32 commits pushed to the master branch:

Your commit was automatically rebased without conflicts.

Pushed as commit 40f847e.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

knisht pushed a commit to knisht/jdk that referenced this pull request Jul 9, 2024
* IDEA-283934 Top panel (toolbar, Editor tabs) hides under the Mac menu in full-screen mode

* IDEA-283934 Top panel (toolbar, Editor tabs) hides under the Mac menu in full-screen mode

* JBR-4305 IDEA-283934 Top panel (toolbar, Editor tabs) hides under the Mac menu in full-screen mode

* JBR-4305 IDEA-283934 Top panel (toolbar, Editor tabs) hides under the Mac menu in full-screen mode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hotspot hotspot-dev@openjdk.org integrated Pull request has been integrated serviceability serviceability-dev@openjdk.org test-request
Development

Successfully merging this pull request may close these issues.

8 participants