Ссылка на прототип OSGi DS не выпущена - PullRequest
0 голосов
/ 16 октября 2018

Я создал простой портлет vaadin в контексте Liferay 7 / DXP или osgi 6, и я заметил, что мои ссылки не собирают мусор, если я использую декларативные службы osgi с областью прототипа, но они делают, если я использую serviceObjects,Почему?

Примечание. Я обновил этот вопрос и поставил в конце еще более простой пример.

Мой основной компонент - это компонент-прототип, имеющий ссылку на прототип объекта.Если я использую декларативные службы osgi для объявления своей зависимости (HelloPresenter в следующем листинге), моя зависимость не будет освобождена и останется в куче навсегда:

import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.UI;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceScope;
import org.osgi.service.component.annotations.ServiceScope;

/**
 * Created by marcel
 */
@Component(
    property = {
        "com.liferay.portlet.display-category=VaadinHelloMvp",
        "javax.portlet.display-name=VaadinHelloMvp",
        "javax.portlet.name=VaadinHelloMvp",
        "com.vaadin.osgi.liferay.portlet-ui=true"
    },
    service = UI.class,
    scope = ServiceScope.PROTOTYPE
)
public class VaadinHelloMvpPortlet extends UI {

  @Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED)
  private HelloPresenter helloPresenter;

  @Override
  protected void init(VaadinRequest request) {
    this.setContent(helloPresenter.getViewComponent());
  }
}

Итак, я попытался получитьмой экземпляр службы для моего HelloPresenter программным способом, который прекрасно работает:

import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.UI;

import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceObjects;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ServiceScope;

/**
 * Created by marcel
 */
@Component(
    property = {
        "com.liferay.portlet.display-category=VaadinHelloMvp",
        "javax.portlet.display-name=VaadinHelloMvp",
        "javax.portlet.name=VaadinHelloMvp",
        "com.vaadin.osgi.liferay.portlet-ui=true"
    },
    service = UI.class,
    scope = ServiceScope.PROTOTYPE
)
public class VaadinHelloMvpPortlet extends UI {

  private HelloPresenter helloPresenter;

  @Override
  protected void init(VaadinRequest request) {
    Bundle bundle = FrameworkUtil.getBundle(HelloPresenter.class);
    ServiceReference<HelloPresenter> serviceReference = bundle.getBundleContext().getServiceReference(HelloPresenter.class);
    ServiceObjects<HelloPresenter> serviceObjects = bundle.getBundleContext().getServiceObjects(serviceReference);
    helloPresenter = serviceObjects.getService();
    this.addDetachListener(event -> serviceObjects.ungetService(helloPresenter));
    helloPresenter.init();
    this.setContent(helloPresenter.getViewComponent());
  }
}

Так что мне интересно, почему мой HelloPresenter не будет выпущен средой osgi в первом сценарии, а во втором?1011 *

Мой объект портлета (UI) также создается с помощью

serviceObjects.getService();

и освобождается с помощью

serviceObjects.ungetService(uiObject);

, и я пробовал другие сценарии, в которых я устанавливал другую ссылку на прототип в своем HelloPresenter, который также создаст ссылку, которая не будет выпущена и сборка мусора.Так что мой опыт заключался в том, что всякий раз, когда вы создаете объект службы, который содержит ссылку на прототип, ссылка не будет освобождена и застревает в куче jvm после освобождения объекта службы

. Поэтому у меня возникла идея, что либоЯ делаю что-то не так или пропускаю параметр, из-за которого моя ссылка на прототип никогда не выпускается ИЛИ что-то не так с смешиванием декларативного сервиса osgi и объектов serviceObjects ...

Знаете ли вы, как я могу заставить работать мой первый пример?Я хочу использовать аннотации, а также быть уверенным, что они закрываются после закрытия моего интерфейса портлета.

ОБНОВЛЕНИЕ

Я создал еще один пример скомпонент singleton для выполнения команды оболочки gogo и объекта-прототипа, который также содержит ссылку на прототип:

@Component(
    service = GogoShellService.class,
    scope = ServiceScope.SINGLETON,
    immediate = true,
    property =
        {
            "osgi.command.scope=test",
            "osgi.command.function=atest",
        }
)
public class GogoShellService {

  public String atest() {
    Bundle bundle = FrameworkUtil.getBundle(APrototypeComponent.class);
    ServiceReference<APrototypeComponent> serviceReference = bundle.getBundleContext().getServiceReference(APrototypeComponent.class);
    ServiceObjects<APrototypeComponent> serviceObjects = bundle.getBundleContext().getServiceObjects(serviceReference);
    APrototypeComponent service = serviceObjects.getService();
    String s = "Hello From: " + service.sayHello();
    serviceObjects.ungetService(service);
    return s;
  }
}

@Component(scope = ServiceScope.PROTOTYPE, service = APrototypeComponent.class, servicefactory = true)
public class APrototypeComponent {

  @Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED)
  AProInAProComp aProInAProComp;

  public String sayHello() {

    String hello = "Hello From " + this.getClass().getSimpleName() + "(" + this.toString() + ") ";
    if (aProInAProComp != null) {
      hello += aProInAProComp.sayHello();
    }

    return hello;
  }
}

@Component(scope = ServiceScope.PROTOTYPE, service = AProInAProComp.class)
public class AProInAProComp {

  public String sayHello() {
    return "Hello From " + this.getClass().getSimpleName() + "(" + this.toString() + ")";
  }
}

Каждый раз, когда я выполняю команду (GogoShellService # atest), создается новый экземпляр прототипа, который также должен быть уничтоженвпоследствии, но я все еще вижу этот объект в своей куче, и запуск сборки мусора не очищает это ...

Вывод отладки osgi следующий:

[org_apache_felix_scr:94] getService  {de.foo.bar.bax.gogo.GogoShellService}={osgi.command.function=atest, component.name=de.foo.bar.bax.gogo.GogoShellService, component.id=2944, osgi.command.scope=test, service.id=7827, service.bundleid=51, service.scope=bundle}: stack of references: [] 
APrototypeComponent(2942)] ServiceFactory.getService() 
AProInAProComp(2941)] ServiceFactory.getService() 
AProInAProComp(2941)] This thread collected dependencies 
AProInAProComp(2941)] getService (ServiceFactory) dependencies collected. 
AProInAProComp(2941)] Querying state active 
AProInAProComp(2941)] Changed state from active to active 
APrototypeComponent(2942)] This thread collected dependencies 
APrototypeComponent(2942)] getService (ServiceFactory) dependencies collected. 
APrototypeComponent(2942)] Querying state satisfied 
APrototypeComponent(2942)] For dependency aProInAProComp, optional: false; to bind: [[MultiplePrototypeRefPair: ref: [{de.foo.bar.bax.checkosgi.AProInAProComp}={component.name=de.foo.bar.bax.checkosgi.AProInAProComp, component.id=2941, service.id=7823, service.bundleid=51, service.scope=prototype}] has service: [true]]] 
APrototypeComponent(2942)] Changed state from satisfied to active 
APrototypeComponent(2942)] ServiceFactory.ungetService() 
APrototypeComponent(2942)] DependencyManager: aProInAProComp close component unbinding from org.apache.felix.scr.impl.manager.ComponentContextImpl@3927bc1d at tracking count 1 refpairs: [[MultiplePrototypeRefPair: ref: [{de.foo.bar.bax.checkosgi.AProInAProComp}={component.name=de.foo.bar.bax.checkosgi.AProInAProComp, component.id=2941, service.id=7823, service.bundleid=51, service.scope=prototype}] has service: [true]]] 
APrototypeComponent(2942)] Querying state active 
APrototypeComponent(2942)] Changed state from active to satisfied 

Надеюсьне понимаю, почему мои прототипы не могут собирать мусор ...

1 Ответ

0 голосов
/ 26 октября 2018

Обновление для новых читателей

Начиная с Apache Felix SCR 2.1.14, эта проблема должна быть исправлена, и совершенно правильный код из исходного вопроса больше не приведет к ошибочному поведению.

ОригиналОтвет

Во-первых, спасибо за работу по созданию простого примера для демонстрации вашего вопроса!

Вы абсолютно правы, что SCR должен выпустить все ссылки вашего компонента после его деактивации.Для ссылки ReferenceScope.PROTOTYPE_REQUIRED в области видимости это должно привести к тому, что экземпляр службы будет освобожден и приведен в порядок.

К сожалению, кажется, что эта функция SCR не работает некоторое время.Я поднял https://issues.apache.org/jira/browse/FELIX-5974 от вашего имени, так что это должно быть исправлено в ближайшее время, но сейчас «легкий» обходной путь - это взять под контроль жизненный цикл самостоятельно.

@Component(
  service = GogoShellService.class,
  scope = ServiceScope.SINGLETON,
  immediate = true,
  property =
    {
      "osgi.command.scope=test",
      "osgi.command.function=atest",
    }
  )
public class GogoShellService {

  public String atest() {
    Bundle bundle = FrameworkUtil.getBundle(APrototypeComponent.class);
    ServiceReference<APrototypeComponent> serviceReference = bundle.getBundleContext().getServiceReference(APrototypeComponent.class);
    ServiceObjects<APrototypeComponent> serviceObjects = bundle.getBundleContext().getServiceObjects(serviceReference);
    APrototypeComponent service = serviceObjects.getService();
    String s = "Hello From: " + service.sayHello();
    serviceObjects.ungetService(service);
    return s;
  }
}

@Component(scope = ServiceScope.PROTOTYPE, service = APrototypeComponent.class, servicefactory = true)
public class APrototypeComponent {

  @Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED)
  ComponentServiceObjects<AProInAProComp> cso;

  AProInAProComp aProInAProComp

  @Activate
  void start() {
    aProInAProComp = cso.getService();
  }

  @Deactivate
  void stop() {
    cso.ungetService(aProInAProComp);
    aProInAProComp = null;
  }

  public String sayHello() {

    String hello = "Hello From " + this.getClass().getSimpleName() + "(" + this.toString() + ") ";

    if (aProInAProComp != null) {
      hello += aProInAProComp.sayHello();
    }

    return hello;
  }
}

@Component(scope = ServiceScope.PROTOTYPE, service = AProInAProComp.class)
public class AProInAProComp {

  public String sayHello() {
    return "Hello From " + this.getClass().getSimpleName() + "(" + this.toString() + ")";
  }
}
...