8383 * </p>
8484 * <p>
8585 * The {@link #onActivation} and {@link #onDeactivation} methods are called by {@link ProfilingActivationListener}
86- * which register an {@link ActivationEvent} in to a {@linkplain #eventBuffer ring buffer} whenever a {@link Span}
86+ * which register an {@link ActivationEvent} to a {@linkplain #eventBuffer ring buffer} whenever a {@link Span}
8787 * gets {@link Span#activate()}d or {@link Span#deactivate()}d while a {@linkplain #profilingSessionOngoing profiling session is ongoing}.
8888 * A background thread consumes the {@link ActivationEvent}s and writes them to a {@linkplain #activationEventsBuffer direct buffer}
8989 * which is flushed to a {@linkplain #activationEventsFileChannel file}.
9090 * That is necessary because within a profiling session (which lasts 10s by default) there may be many more {@link ActivationEvent}s
91- * than the ring buffer can hold {@link #RING_BUFFER_SIZE}.
91+ * than the ring buffer {@link #RING_BUFFER_SIZE can hold }.
9292 * The file can hold {@link #ACTIVATION_EVENTS_IN_FILE} events and each is {@link ActivationEvent#SERIALIZED_SIZE} in size.
9393 * This process is completely garbage free thanks to the {@link RingBuffer} acting as an object pool for {@link ActivationEvent}s.
9494 * </p>
@@ -335,12 +335,15 @@ public void run() {
335335 if (!config .isProfilingEnabled () || !tracer .isRunning ()) {
336336 if (jfrParser != null ) {
337337 jfrParser = null ;
338- rootPool .clear ();
339- callTreePool .clear ();
340338 }
341339 if (!scheduler .isShutdown ()) {
342340 scheduler .schedule (this , config .getProfilingInterval ().getMillis (), TimeUnit .MILLISECONDS );
343341 }
342+ try {
343+ clear ();
344+ } catch (Throwable throwable ) {
345+ logger .error ("Error while trying to clear profiler constructs" , throwable );
346+ }
344347 return ;
345348 }
346349
@@ -391,6 +394,8 @@ private void profile(TimeDuration sampleRate, TimeDuration profilingDuration) th
391394 if (!profiledThreads .isEmpty ()) {
392395 restoreFilterState (asyncProfiler );
393396 }
397+ // Doesn't need to be atomic as this field is being updated only by a single thread
398+ //noinspection NonAtomicOperationOnVolatileField
394399 profilingSessions ++;
395400
396401 // When post-processing is disabled activation events are ignored, but we still need to invoke this method
@@ -418,17 +423,22 @@ private void profile(TimeDuration sampleRate, TimeDuration profilingDuration) th
418423 * we have to tell async-profiler which threads it should profile after re-starting it.
419424 */
420425 private void restoreFilterState (AsyncProfiler asyncProfiler ) {
421- threadMatcher .forEachThread (new ThreadMatcher .NonCapturingPredicate <Thread , Long2ObjectHashMap <?>.KeySet >() {
422- @ Override
423- public boolean test (Thread thread , Long2ObjectHashMap <?>.KeySet profiledThreads ) {
424- return profiledThreads .contains (thread .getId ());
425- }
426- }, profiledThreads .keySet (), new ThreadMatcher .NonCapturingConsumer <Thread , AsyncProfiler >() {
427- @ Override
428- public void accept (Thread thread , AsyncProfiler asyncProfiler ) {
429- asyncProfiler .enableProfilingThread (thread );
430- }
431- }, asyncProfiler );
426+ threadMatcher .forEachThread (
427+ new ThreadMatcher .NonCapturingPredicate <Thread , Long2ObjectHashMap <?>.KeySet >() {
428+ @ Override
429+ public boolean test (Thread thread , Long2ObjectHashMap <?>.KeySet profiledThreads ) {
430+ return profiledThreads .contains (thread .getId ());
431+ }
432+ },
433+ profiledThreads .keySet (),
434+ new ThreadMatcher .NonCapturingConsumer <Thread , AsyncProfiler >() {
435+ @ Override
436+ public void accept (Thread thread , AsyncProfiler asyncProfiler ) {
437+ asyncProfiler .enableProfilingThread (thread );
438+ }
439+ },
440+ asyncProfiler
441+ );
432442 }
433443
434444 private void consumeActivationEventsFromRingBufferAndWriteToFile (TimeDuration profilingDuration ) throws Exception {
@@ -623,7 +633,9 @@ private static long peekLong(ByteBuffer buf) {
623633
624634 public void resetActivationEventBuffer () throws IOException {
625635 ((Buffer ) activationEventsBuffer ).clear ();
626- activationEventsFileChannel .position (0L );
636+ if (activationEventsFileChannel != null && activationEventsFileChannel .isOpen ()) {
637+ activationEventsFileChannel .position (0L );
638+ }
627639 }
628640
629641 private void flushActivationEvents () throws IOException {
@@ -691,7 +703,7 @@ void setProfilingSessionOngoing(boolean profilingSessionOngoing) {
691703
692704 public void clearProfiledThreads () {
693705 for (CallTree .Root root : profiledThreads .values ()) {
694- root .recycle (callTreePool );
706+ root .recycle (callTreePool , rootPool );
695707 }
696708 profiledThreads .clear ();
697709 }
@@ -702,7 +714,6 @@ CallTree.Root getRoot() {
702714 }
703715
704716 void clear () throws IOException {
705- profiledThreads .clear ();
706717 // consume all remaining events from the ring buffer
707718 try {
708719 poller .poll (new EventPoller .Handler <ActivationEvent >() {
@@ -716,6 +727,9 @@ public boolean onEvent(ActivationEvent event, long sequence, boolean endOfBatch)
716727 throw new RuntimeException (e );
717728 }
718729 resetActivationEventBuffer ();
730+ profiledThreads .clear ();
731+ callTreePool .clear ();
732+ rootPool .clear ();
719733 }
720734
721735 int getProfilingSessions () {
@@ -834,8 +848,7 @@ private void startProfiling(SamplingProfiler samplingProfiler) {
834848 if (logger .isDebugEnabled ()) {
835849 logger .warn ("Illegal state when stopping profiling for thread {}: orphaned root" , threadId );
836850 }
837- orphaned .recycle (samplingProfiler .callTreePool );
838- samplingProfiler .rootPool .recycle (orphaned );
851+ orphaned .recycle (samplingProfiler .callTreePool , samplingProfiler .rootPool );
839852 }
840853 }
841854
@@ -867,17 +880,19 @@ private void stopProfiling(SamplingProfiler samplingProfiler) {
867880 logger .debug ("End call tree ({}) for thread {}" , deserialize (samplingProfiler , traceContextBuffer ), threadId );
868881 }
869882 samplingProfiler .profiledThreads .remove (threadId );
870- callTree .end (samplingProfiler .callTreePool , samplingProfiler .getInferredSpansMinDurationNs ());
871- int createdSpans = callTree .spanify ();
872- if (logger .isDebugEnabled ()) {
873- if (createdSpans > 0 ) {
874- logger .debug ("Created spans ({}) for thread {}" , createdSpans , threadId );
875- } else {
876- logger .debug ("Created no spans for thread {} (count={})" , threadId , callTree .getCount ());
883+ try {
884+ callTree .end (samplingProfiler .callTreePool , samplingProfiler .getInferredSpansMinDurationNs ());
885+ int createdSpans = callTree .spanify ();
886+ if (logger .isDebugEnabled ()) {
887+ if (createdSpans > 0 ) {
888+ logger .debug ("Created spans ({}) for thread {}" , createdSpans , threadId );
889+ } else {
890+ logger .debug ("Created no spans for thread {} (count={})" , threadId , callTree .getCount ());
891+ }
877892 }
893+ } finally {
894+ callTree .recycle (samplingProfiler .callTreePool , samplingProfiler .rootPool );
878895 }
879- callTree .recycle (samplingProfiler .callTreePool );
880- samplingProfiler .rootPool .recycle (callTree );
881896 }
882897 }
883898
0 commit comments