8210330: Make CLD claiming allow multiple claim bits
authoreosterlund
Tue, 16 Oct 2018 13:16:11 +0200
changeset 52141 de6dc206a92b
parent 52140 3a168f782e80
child 52142 ca0c25e01c5b
8210330: Make CLD claiming allow multiple claim bits Reviewed-by: pliden, coleenp
src/hotspot/share/classfile/classLoaderData.cpp
src/hotspot/share/classfile/classLoaderData.hpp
src/hotspot/share/classfile/classLoaderDataGraph.cpp
src/hotspot/share/gc/cms/cmsOopClosures.inline.hpp
src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp
src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp
src/hotspot/share/gc/g1/g1FullGCMarker.cpp
src/hotspot/share/gc/g1/g1HeapVerifier.cpp
src/hotspot/share/gc/g1/g1OopClosures.cpp
src/hotspot/share/gc/g1/g1OopClosures.hpp
src/hotspot/share/gc/g1/g1RootClosures.cpp
src/hotspot/share/gc/g1/g1SharedClosures.hpp
src/hotspot/share/gc/parallel/pcTasks.cpp
src/hotspot/share/gc/parallel/psParallelCompact.cpp
src/hotspot/share/gc/serial/defNewGeneration.cpp
src/hotspot/share/gc/serial/markSweep.cpp
src/hotspot/share/gc/z/zRootsIterator.cpp
src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp
src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp
src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.cpp
src/hotspot/share/memory/iterator.cpp
src/hotspot/share/memory/iterator.hpp
src/hotspot/share/memory/iterator.inline.hpp
--- a/src/hotspot/share/classfile/classLoaderData.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/classfile/classLoaderData.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -139,7 +139,7 @@
   // it from being unloaded during parsing of the unsafe anonymous class.
   // The null-class-loader should always be kept alive.
   _keep_alive((is_unsafe_anonymous || h_class_loader.is_null()) ? 1 : 0),
-  _claimed(0),
+  _claim(0),
   _handles(),
   _klasses(NULL), _packages(NULL), _modules(NULL), _unnamed_module(NULL), _dictionary(NULL),
   _jmethod_ids(NULL),
@@ -268,12 +268,17 @@
 }
 #endif // PRODUCT
 
-bool ClassLoaderData::claim() {
-  if (_claimed == 1) {
-    return false;
+bool ClassLoaderData::try_claim(int claim) {
+  for (;;) {
+    int old_claim = Atomic::load(&_claim);
+    if ((old_claim & claim) == claim) {
+      return false;
+    }
+    int new_claim = old_claim | claim;
+    if (Atomic::cmpxchg(new_claim, &_claim, old_claim) == old_claim) {
+      return true;
+    }
   }
-
-  return (int) Atomic::cmpxchg(1, &_claimed, 0) == 0;
 }
 
 // Unsafe anonymous classes have their own ClassLoaderData that is marked to keep alive
@@ -295,8 +300,8 @@
   }
 }
 
-void ClassLoaderData::oops_do(OopClosure* f, bool must_claim, bool clear_mod_oops) {
-  if (must_claim && !claim()) {
+void ClassLoaderData::oops_do(OopClosure* f, int claim_value, bool clear_mod_oops) {
+  if (claim_value != ClassLoaderData::_claim_none && !try_claim(claim_value)) {
     return;
   }
 
--- a/src/hotspot/share/classfile/classLoaderData.hpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/classfile/classLoaderData.hpp	Tue Oct 16 13:16:11 2018 +0200
@@ -128,9 +128,8 @@
                            // loader. _keep_alive does not need to be volatile or
                            // atomic since there is one unique CLD per unsafe anonymous class.
 
-  volatile int _claimed;   // true if claimed, for example during GC traces.
-                           // To avoid applying oop closure more than once.
-                           // Has to be an int because we cas it.
+  volatile int _claim; // non-zero if claimed, for example during GC traces.
+                       // To avoid applying oop closure more than once.
   ChunkedHandleList _handles; // Handles to constant pool arrays, Modules, etc, which
                               // have the same life cycle of the corresponding ClassLoader.
 
@@ -200,11 +199,22 @@
   Dictionary* create_dictionary();
 
   void initialize_name(Handle class_loader);
+
  public:
   // GC interface.
-  void clear_claimed() { _claimed = 0; }
-  bool claimed() const { return _claimed == 1; }
-  bool claim();
+
+  // The "claim" is typically used to check if oops_do needs to be applied on
+  // the CLD or not. Most GCs only perform strong marking during the marking phase.
+  enum {
+    _claim_none        = 0,
+    _claim_finalizable = 2,
+    _claim_strong      = 3
+  };
+  void clear_claim() { _claim = 0; }
+  bool claimed() const { return _claim != 0; }
+  bool try_claim(int claim);
+  int get_claim() const { return _claim; }
+  void set_claim(int claim) { _claim = claim; }
 
   // Computes if the CLD is alive or not. This is safe to call in concurrent
   // contexts.
@@ -264,7 +274,7 @@
 
   void initialize_holder(Handle holder);
 
-  void oops_do(OopClosure* f, bool must_claim, bool clear_modified_oops = false);
+  void oops_do(OopClosure* f, int claim_value, bool clear_modified_oops = false);
 
   void classes_do(KlassClosure* klass_closure);
   Klass* klasses() { return _klasses; }
--- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -48,7 +48,7 @@
 
 void ClassLoaderDataGraph::clear_claimed_marks() {
   for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
-    cld->clear_claimed();
+    cld->clear_claim();
   }
 }
 
--- a/src/hotspot/share/gc/cms/cmsOopClosures.inline.hpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/cms/cmsOopClosures.inline.hpp	Tue Oct 16 13:16:11 2018 +0200
@@ -41,8 +41,7 @@
 }
 
 inline void MetadataVisitingOopsInGenClosure::do_cld(ClassLoaderData* cld) {
-  bool claim = true;  // Must claim the class loader data before processing.
-  cld->oops_do(this, claim);
+  cld->oops_do(this, ClassLoaderData::_claim_strong);
 }
 
 // Decode the oop and call do_oop on it.
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -2398,7 +2398,7 @@
  public:
   VerifyCLDOopsCLDClosure(CMSBitMap* bitmap) : _oop_closure(bitmap) {}
   void do_cld(ClassLoaderData* cld) {
-    cld->oops_do(&_oop_closure, false, false);
+    cld->oops_do(&_oop_closure, ClassLoaderData::_claim_none, false);
   }
 };
 
@@ -2413,7 +2413,7 @@
   // Mark from roots one level into CMS
   MarkRefsIntoVerifyClosure notOlder(_span, verification_mark_bm(),
                                      markBitMap());
-  CLDToOopClosure cld_closure(&notOlder, true);
+  CLDToOopClosure cld_closure(&notOlder, ClassLoaderData::_claim_strong);
 
   heap->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
 
@@ -2886,7 +2886,7 @@
       }
     } else {
       // The serial version.
-      CLDToOopClosure cld_closure(&notOlder, true);
+      CLDToOopClosure cld_closure(&notOlder, ClassLoaderData::_claim_strong);
       heap->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel.
 
       StrongRootsScope srs(1);
@@ -4269,7 +4269,7 @@
   _timer.reset();
   _timer.start();
 
-  CLDToOopClosure cld_closure(&par_mri_cl, true);
+  CLDToOopClosure cld_closure(&par_mri_cl, ClassLoaderData::_claim_strong);
 
   heap->cms_process_roots(_strong_roots_scope,
                           false,     // yg was scanned above
@@ -4331,7 +4331,7 @@
 class RemarkCLDClosure : public CLDClosure {
   CLDToOopClosure _cm_closure;
  public:
-  RemarkCLDClosure(OopClosure* oop_closure) : _cm_closure(oop_closure) {}
+  RemarkCLDClosure(OopClosure* oop_closure) : _cm_closure(oop_closure, ClassLoaderData::_claim_strong) {}
   void do_cld(ClassLoaderData* cld) {
     // Check if we have modified any oops in the CLD during the concurrent marking.
     if (cld->has_accumulated_modified_oops()) {
--- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -108,7 +108,7 @@
   AlwaysTrueClosure always_alive;
   _weak_proc_task.work(worker_id, &always_alive, &_adjust);
 
-  CLDToOopClosure adjust_cld(&_adjust);
+  CLDToOopClosure adjust_cld(&_adjust, ClassLoaderData::_claim_strong);
   CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations);
   _root_processor.process_all_roots(
       &_adjust,
--- a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "classfile/classLoaderData.hpp"
 #include "gc/g1/g1FullGCMarker.inline.hpp"
 #include "gc/shared/referenceProcessor.hpp"
 #include "memory/iterator.inline.hpp"
@@ -36,7 +37,7 @@
     _mark_closure(worker_id, this, G1CollectedHeap::heap()->ref_processor_stw()),
     _verify_closure(VerifyOption_G1UseFullMarking),
     _stack_closure(this),
-    _cld_closure(mark_closure()) {
+    _cld_closure(mark_closure(), ClassLoaderData::_claim_strong) {
   _oop_stack.initialize();
   _objarray_stack.initialize();
 }
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -170,10 +170,10 @@
  public:
   VerifyCLDClosure(G1CollectedHeap* g1h, OopClosure* cl) : _young_ref_counter_closure(g1h), _oop_closure(cl) {}
   void do_cld(ClassLoaderData* cld) {
-    cld->oops_do(_oop_closure, false);
+    cld->oops_do(_oop_closure, ClassLoaderData::_claim_none);
 
     _young_ref_counter_closure.reset_count();
-    cld->oops_do(&_young_ref_counter_closure, false);
+    cld->oops_do(&_young_ref_counter_closure, ClassLoaderData::_claim_none);
     if (_young_ref_counter_closure.count() > 0) {
       guarantee(cld->has_modified_oops(), "CLD " PTR_FORMAT ", has young %d refs but is not dirty.", p2i(cld), _young_ref_counter_closure.count());
     }
--- a/src/hotspot/share/gc/g1/g1OopClosures.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/g1/g1OopClosures.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -52,7 +52,7 @@
 
     // Clean the cld since we're going to scavenge all the metadata.
     // Clear modified oops only if this cld is claimed.
-    cld->oops_do(_closure, _must_claim, /*clear_modified_oops*/true);
+    cld->oops_do(_closure, _claim, /*clear_modified_oops*/true);
 
     _closure->set_scanned_cld(NULL);
 
--- a/src/hotspot/share/gc/g1/g1OopClosures.hpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/g1/g1OopClosures.hpp	Tue Oct 16 13:16:11 2018 +0200
@@ -160,12 +160,12 @@
 class G1CLDScanClosure : public CLDClosure {
   G1ParCopyHelper* _closure;
   bool             _process_only_dirty;
-  bool             _must_claim;
+  int              _claim;
   int              _count;
 public:
   G1CLDScanClosure(G1ParCopyHelper* closure,
-                   bool process_only_dirty, bool must_claim)
-  : _closure(closure), _process_only_dirty(process_only_dirty), _must_claim(must_claim), _count(0) {}
+                   bool process_only_dirty, int claim_value)
+  : _closure(closure), _process_only_dirty(process_only_dirty), _claim(claim_value), _count(0) {}
   void do_cld(ClassLoaderData* cld);
 };
 
--- a/src/hotspot/share/gc/g1/g1RootClosures.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/g1/g1RootClosures.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -35,7 +35,7 @@
   G1EvacuationClosures(G1CollectedHeap* g1h,
                        G1ParScanThreadState* pss,
                        bool in_young_gc) :
-      _closures(g1h, pss, in_young_gc, /* must_claim_cld */ false) {}
+      _closures(g1h, pss, in_young_gc, /* cld_claim */ ClassLoaderData::_claim_none) {}
 
   OopClosure* weak_oops()   { return &_closures._oops; }
   OopClosure* strong_oops() { return &_closures._oops; }
@@ -73,8 +73,8 @@
 public:
   G1InitialMarkClosures(G1CollectedHeap* g1h,
                         G1ParScanThreadState* pss) :
-      _strong(g1h, pss, /* process_only_dirty_klasses */ false, /* must_claim_cld */ true),
-      _weak(g1h, pss,   /* process_only_dirty_klasses */ false, /* must_claim_cld */ true) {}
+      _strong(g1h, pss, /* process_only_dirty_klasses */ false, /* cld_claim */ ClassLoaderData::_claim_strong),
+      _weak(g1h, pss,   /* process_only_dirty_klasses */ false, /* cld_claim */ ClassLoaderData::_claim_strong) {}
 
   OopClosure* weak_oops()   { return &_weak._oops; }
   OopClosure* strong_oops() { return &_strong._oops; }
--- a/src/hotspot/share/gc/g1/g1SharedClosures.hpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/g1/g1SharedClosures.hpp	Tue Oct 16 13:16:11 2018 +0200
@@ -39,9 +39,9 @@
   G1CLDScanClosure                _clds;
   G1CodeBlobClosure               _codeblobs;
 
-  G1SharedClosures(G1CollectedHeap* g1h, G1ParScanThreadState* pss, bool process_only_dirty, bool must_claim_cld) :
+  G1SharedClosures(G1CollectedHeap* g1h, G1ParScanThreadState* pss, bool process_only_dirty, int cld_claim) :
     _oops(g1h, pss),
     _oops_in_cld(g1h, pss),
-    _clds(&_oops_in_cld, process_only_dirty, must_claim_cld),
+    _clds(&_oops_in_cld, process_only_dirty, cld_claim),
     _codeblobs(&_oops) {}
 };
--- a/src/hotspot/share/gc/parallel/pcTasks.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/parallel/pcTasks.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -110,7 +110,7 @@
       break;
 
     case class_loader_data: {
-        CLDToOopClosure cld_closure(&mark_and_push_closure);
+        CLDToOopClosure cld_closure(&mark_and_push_closure, ClassLoaderData::_claim_strong);
         ClassLoaderDataGraph::always_strong_cld_do(&cld_closure);
       }
       break;
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -2213,7 +2213,7 @@
   Management::oops_do(&oop_closure);
   JvmtiExport::oops_do(&oop_closure);
   SystemDictionary::oops_do(&oop_closure);
-  CLDToOopClosure cld_closure(&oop_closure);
+  CLDToOopClosure cld_closure(&oop_closure, ClassLoaderData::_claim_strong);
   ClassLoaderDataGraph::cld_do(&cld_closure);
 
   // Now adjust pointers in remaining weak roots.  (All of which should
--- a/src/hotspot/share/gc/serial/defNewGeneration.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -137,7 +137,7 @@
     _scavenge_closure->set_scanned_cld(cld);
 
     // Clean the cld since we're going to scavenge all the metadata.
-    cld->oops_do(_scavenge_closure, false, /*clear_modified_oops*/true);
+    cld->oops_do(_scavenge_closure, ClassLoaderData::_claim_none, /*clear_modified_oops*/true);
 
     _scavenge_closure->set_scanned_cld(NULL);
   }
--- a/src/hotspot/share/gc/serial/markSweep.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/serial/markSweep.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -58,9 +58,9 @@
 
 MarkSweep::FollowRootClosure  MarkSweep::follow_root_closure;
 
-MarkAndPushClosure            MarkSweep::mark_and_push_closure;
-CLDToOopClosure               MarkSweep::follow_cld_closure(&mark_and_push_closure);
-CLDToOopClosure               MarkSweep::adjust_cld_closure(&adjust_pointer_closure);
+MarkAndPushClosure MarkSweep::mark_and_push_closure;
+CLDToOopClosure    MarkSweep::follow_cld_closure(&mark_and_push_closure, ClassLoaderData::_claim_strong);
+CLDToOopClosure    MarkSweep::adjust_cld_closure(&adjust_pointer_closure, ClassLoaderData::_claim_strong);
 
 template <class T> inline void MarkSweep::KeepAliveClosure::do_oop_work(T* p) {
   mark_and_push(p);
--- a/src/hotspot/share/gc/z/zRootsIterator.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/gc/z/zRootsIterator.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -242,7 +242,7 @@
 
 void ZConcurrentRootsIterator::do_class_loader_data_graph(ZRootsIteratorClosure* cl) {
   ZStatTimer timer(ZSubPhaseConcurrentRootsClassLoaderDataGraph);
-  CLDToOopClosure cld_cl(cl, _marking /* must_claim */);
+  CLDToOopClosure cld_cl(cl, _marking ? ClassLoaderData::_claim_strong : ClassLoaderData::_claim_none);
   ClassLoaderDataGraph::cld_do(&cld_cl);
 }
 
--- a/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -92,7 +92,7 @@
   SaveRestoreCLDClaimBits save_restore_cld_claim_bits;
   RootSetClosureMarkScope mark_scope;
 
-  CLDToOopClosure cldt_closure(closure);
+  CLDToOopClosure cldt_closure(closure, ClassLoaderData::_claim_strong);
   ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure);
   CodeBlobToOopClosure blobs(closure, false);
   Threads::oops_do(closure, &blobs);
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -128,7 +128,7 @@
 bool ReferenceToRootClosure::do_cldg_roots() {
   assert(!complete(), "invariant");
   ReferenceLocateClosure rlc(_callback, OldObjectRoot::_class_loader_data, OldObjectRoot::_type_undetermined, NULL);
-  CLDToOopClosure cldt_closure(&rlc);
+  CLDToOopClosure cldt_closure(&rlc, ClassLoaderData::_claim_strong);
   ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure);
   return rlc.complete();
 }
--- a/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -69,12 +69,12 @@
 
 CLDClaimContext::CLDClaimContext(ClassLoaderData* cld) : _cld(cld) {
   assert(_cld->claimed(), "invariant");
-  _cld->clear_claimed();
+  _cld->clear_claim();
 }
 
 CLDClaimContext::~CLDClaimContext() {
   if (_cld != NULL) {
-    _cld->claim();
+    _cld->try_claim(ClassLoaderData::_claim_strong);
     assert(_cld->claimed(), "invariant");
   }
 }
--- a/src/hotspot/share/memory/iterator.cpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/memory/iterator.cpp	Tue Oct 16 13:16:11 2018 +0200
@@ -32,7 +32,7 @@
 DoNothingClosure do_nothing_cl;
 
 void CLDToOopClosure::do_cld(ClassLoaderData* cld) {
-  cld->oops_do(_oop_closure, _must_claim_cld);
+  cld->oops_do(_oop_closure, _cld_claim);
 }
 
 void ObjectToOopClosure::do_object(oop obj) {
--- a/src/hotspot/share/memory/iterator.hpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/memory/iterator.hpp	Tue Oct 16 13:16:11 2018 +0200
@@ -127,12 +127,13 @@
 
 class CLDToOopClosure : public CLDClosure {
   OopClosure*       _oop_closure;
-  bool              _must_claim_cld;
+  int               _cld_claim;
 
  public:
-  CLDToOopClosure(OopClosure* oop_closure, bool must_claim_cld = true) :
+  CLDToOopClosure(OopClosure* oop_closure,
+                  int cld_claim) :
       _oop_closure(oop_closure),
-      _must_claim_cld(must_claim_cld) {}
+      _cld_claim(cld_claim) {}
 
   void do_cld(ClassLoaderData* cld);
 };
--- a/src/hotspot/share/memory/iterator.inline.hpp	Tue Oct 16 13:14:18 2018 +0200
+++ b/src/hotspot/share/memory/iterator.inline.hpp	Tue Oct 16 13:16:11 2018 +0200
@@ -39,8 +39,7 @@
 #include "utilities/debug.hpp"
 
 inline void MetadataVisitingOopIterateClosure::do_cld(ClassLoaderData* cld) {
-  bool claim = true;  // Must claim the class loader data before processing.
-  cld->oops_do(this, claim);
+  cld->oops_do(this, ClassLoaderData::_claim_strong);
 }
 
 inline void MetadataVisitingOopIterateClosure::do_klass(Klass* k) {