Можно хранить вместе с процедурной переменной ссылку на модуль и проверять его счётчик выгрузки.
Это для тех сервисов (типа подписки на уведомления), которые не знают, какую процедурную переменную им всунули - и когда будет выгружен модуль.

Для плотно и постоянно связанных компонентов надо просто понимать порядок загрузки-выгрузки. А "на горячую" вообще аккуратно использовать.

## Код:
```
MODULE L1ProcRefs;
   IMPORT Kernel, S := SYSTEM,
      Mem := L1Mem;

   TYPE
      Ref* = RECORD
         mod: Kernel.Module;
         procOffs: INTEGER;
         type: Kernel.Type
      END;

   PROCEDURE (VAR ref: Ref) Init* (IN var: ANYREC), NEW;
      VAR t, ft: Kernel.Type;
            ptr: INTEGER;
   BEGIN
      ASSERT(ref.mod = NIL, 20);
      t := S.VAL(Kernel.Type, S.TYP(var));
      ASSERT(t.fields.num = 1, 20);
      ft := t.fields.obj[0].struct;
      ASSERT(S.VAL(Mem.NativeInt, ft) > 1024, 21);
      ASSERT(ft.id MOD 4 = 0, 21);
      S.GET(S.ADR(var) + t.fields.obj[0].offs, ptr);
      ASSERT(ptr # 0, 22);
      ref.mod := NIL; ref.procOffs := 0; ref.type := t;
      Kernel.SearchProcVar(ptr, ref.mod, ref.procOffs)
   END Init;

   PROCEDURE (VAR ref: Ref) Close*, NEW;
   BEGIN
      ref.mod := NIL; ref.procOffs := 0; ref.type := NIL
   END Close;

   PROCEDURE (IN ref: Ref) NotNil* (): BOOLEAN, NEW;
   BEGIN
   RETURN ref.mod # NIL
   END NotNil;

   PROCEDURE (IN ref: Ref) Valid* (): BOOLEAN, NEW;
   BEGIN
   RETURN ref.mod.refcnt >= 0
   END Valid;

   PROCEDURE (IN ref: Ref) Get* (VAR var: ANYREC), NEW;
   BEGIN
      ASSERT(S.TYP(var) = S.VAL(Mem.NativeInt, ref.type), 20);
      IF ref.mod.refcnt >= 0 THEN
         S.PUT(S.ADR(var) + ref.type.fields.obj[0].offs, ref.mod.code + ref.procOffs);
      ELSE
         S.PUT(S.ADR(var) + ref.type.fields.obj[0].offs, 0)
      END
   END Get;

   PROCEDURE (IN ref: Ref) GetAs* (VAR val: ANYREC), NEW;
   BEGIN
      (* ср. через Low.EqualSignature *)
   END GetAs;

END L1ProcRefs.
```


Использование: в качестве val - RECORD с единственным полем - процедурным.