Newsgroup: it.comp.java
Titolo: servlet e garbage collection

Ciao a tutti,
ho realizzato una classe che estende HttpServlet (chiamiamola Classe), che 
viene istanziata da altre servlet nei loro metodi doGet e doPost.
Mi è venuto però un dubbio. Una volta che le servlet che fanno uso di tale 
classe hanno terminato la richiesta, e i metodi doGet o doPost sono stati 
eseguiti, le istanze di Classe vengono eliminate dalla garbage collection 
oppure rimangono in memoria perchè istanziando una servlet vengono messi in 
esecuzione dei Thread (intasando così la memoria a lungo andare)?
A dire il vero non mi interessa che Classe sia una servlet, l'unico motivo 
per cui gli faccio estendere HttpServlet è che devo prelevare un parametro 
dal contesto delle servlet (lo so, potrei passare tale parametro al metodo 
costruttore di Classe, ma mi è sembrata una soluzione migliore fare in modo 
che Classe sia capace di prelevare questo parametro da sola).
Esiste un modo per prelevare un parametro dal contesto delle servlet da una 
classe che non è una servlet?
Ciao

 classe che non è una servlet?
prelevalo dalla Srvlet (cosa ci satnno a fare?) e passa un riferimento 
ad un a normale classe Java per l'elaborazione.
ciao, HTH
uL

 dal Context
Dinuovo, condivido. Se quel metodo che ti serve è definito su una servlet, è
abbastanza ragionevole pensare che ci sia qualcosa di sbagliato se ti serve
in un contesto differente da una servlet.

 esecuzione dei Thread (intasando così la memoria a lungo andare)?
Esiste un modo "sporco" per impedire che una classe venga garbage collected,
basta rialzare un'eccezione nel metodo finalize, ma è un pessima pratica,
perché induce a memory leak...
Finché hai un reference all'istanza della servlet il garbage collector non
la sfiorerà. Potresti pensare a qualcosa del genere :
public class MiaServlet extends ...{
    // ....
    private static MiaServlet instance = null;
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        MiaServlet.instance = this;
    }
    public static MiaServlet getInstance() throws InitializationException {
       if (null == instance)  {
          throw new InitializationException(
              "La servlet non è stata instanziata e configurata dal
container");
       }
       return instance;
    }
}
In questo modo quando il container instanzia e quindi inizializza la tua
servlet viene salvato il reference all'istanza che potrai richiedere al
metodo getInstance. Se la servlet non è stata inizializzata... eccezione !!
oppure potresti addirittura forzare l'inizializzazione della servlet aprendo
un URL alla servlet stessa.
Essendoci un reference statico all'istanza, dovrebbe esser sufficiente a
mantener lontano il gc.
Non è molto 'pulita', ma... volevo partecipare ;-)
Jigi


Questa e' ancora piu' sporca :-D, sfrutta il fatto che l'oggetto Properties viola LSP (la
classe Properties estende malamente Hashtable).
Non usarla mai!!!
Ciao,
    Bruno.
---
| Bruno Bossola
| A Java Software Engineer :-)
| 
http://digilander.iol.it/bbossola

 la classe che la sua istanza statica.
Vero. Non ci avevo pensato.

Vorrei chiedere un ulteriore chiarimento (scusate, ma non sono un esperto!).
Da quello che ho capito, quando una servlet riceve una richiesta, il 
Container crea una istanza della servlet (sempre che non sia già stata 
istanziata da una precedente richiesta) ed esegue il corrispondente metodo 
doGet o doPost. Al termine della richiesta, l'istanza non viene eliminata 
dalla garbage collection perchè il container contiene un suo riferimento.
Se però succede una cosa del genere:
public class MiaServlet extends HttpServlet {
 public void doGet(...) {
  AltraServlet altra=new Altra(...);
  ...
 }
}
l'istanza di AltraServlet altra non viene istanziata dal container, ma da 
una servlet, quindi il container non contiene un suo riferimento, e al 
termine del metodo doGet l'oggetto altra dovrebbe essere eliminato (è 
questo quello che voglio).
Se sbaglio correggetemi...
Ciao!
 

 questo quello che voglio).
Dal codice che hai postato il reference all'oggetto creato è contenuto nello
scope della doGet, quindi effettivamente al termine del metodo l'oggetto
"altra" è <i>elegible</i> per il garbage collector, il che non significa che
verrà immediatamente rimosso dalla memoria, bensì verrà "marchiato" come "da
rimuovere" al prossimo giro del gc.

 di performance.
Io sto sviluppando una web application, usando JSP, JavaBeans e Tomcat
come servlet container.
Una cosa non mi è chiara: quando un utente chiude il client (il
browser, nel mio caso), le risorse occupate da Tomcat non scendono, ma
se si apre un nuovo client, non vengono riutilizzate quelle risorse,
bensì ne vengono allocate di nuove, e quando il secondo client viene
chiuso, esse rimangono ancora allocate.
In pratica, se nel giro di 10 minuti vengono aperti 200 client e poi
chiusi, la memoria allocata si aggira su 100 MB... rimane lì... e non
è bello.
Se "tutto va come deve andare", quale dovrebbe essere il comportamento
del servlet container? Rilasciare le risorse dopo un tot di tempo?
Riutilizzare quelle lasciate dal client precedente?
Grazie, saluti
Fabio B

 Riutilizzare quelle lasciate dal client precedente?
Garbage Collector... č questo il "come deve andare" per gli oggetti che
allochi per il client, a meno che non ne fai un'uso tale da rendere
impossibile la restituzione della memoria.
Ogni servlet ha un'istanza, e in alcuni casi un pool di istanze, ma non ne
viene allocata una per ogni richiesta.
Credo che dovresti verificare che il comportamento di Tomcat sia davvero
quello che dici... se č davvero cosě dovresti sottoporre il tuo codice ad un
esperto, probabilmente ti trovera qualche falla nel codice.

OK grazie!
Ciao :-)

 esperto, probabilmente ti trovera qualche falla nel codice.
Se lo sottopongo qui qualcuno se la sente di darmi almeno un parere?
Grazie

 Se lo sottopongo qui qualcuno se la sente di darmi almeno un parere?
Perché no. Ma, viste le tue ulteriori spiegazioni, mi sembra che il tuo
applicativo si comporti correttamente.
Luigi

Consideriamo il seguente esempio, per chiarire: 
public class wbPIPPO extends oracle.jdeveloper.html.DataWebBeanImpl
{
private xUtils u = new xUtils();  //xUtils è una classe che ho
definito altrove
protected Hashtable aaaList = new Hashtable(20);
protected Hashtable bbbList = new Hashtable(10);
protected Hashtable cccList = new Hashtable(10);
protected String[] sArray; 
protected SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd");
protected int irec;
  public void xxx
  {
  ...
  }
  public void yyy
  {
  ...
  }
//fine classe
}
Nelle due funzioni "xxx" e "yyy" utilizzo le variabili create a
livello di classe (stringhe, array, hashtables). Ho dichiarato queste
variabili come protected, poiché ho altre classi che estendono wbPIPPO
(e quindi ho bisogno di avere a disposizione anche queste variabili).
Domanda semplice: come vedete, in questo caso non pongo a null nessuna
variabile prima dell'ultima parentesi graffa (la fine della classe).
Pens(av)o che le variabili venissero distrutte appena la classe fosse
messa a null (in questo caso è un javabean, quindi quando smetto di
usarlo in una pagina JSP).
Secondo voi devo comunque mettere a null anche le variabili create
all'interno di questa classe?

 all'interno di questa classe?
No: quando perdi il reference ad un oggetto *generalmente* perdi il
riferimento anche a tutti gli oggetti contenuti in esso (a meno che non te
li sia salvati da qualche parte). Così che quando quell'oggetto viene
ripulito, vengono ripuliti anche gli oggetti contenuti, o se hai riferimenti
a questi oggetti all'esterno, verranno "risparmiati".
Devi pensare a questo: se un oggetto è utilizzato/utilizzabile (poiché hai
un reference salvato da qualche parte) non verrà rimosso dalla memoria,
altrimenti sì. In genere ad un'implementazione di una buona VM corrisponde
una buona implementazione di un GC, quindi, imparati alcuni concetti
fondamentali sul GC, puoi star sicuro che la tua memoria verrà usata al
meglio. Ti consiglio comunque di approfondire l'argomento. Non ricordo
nessun capitolo sul TIJ dedicato al GC, ma penso che il Java Language Spec o
il VM Spec coprano in dettaglio l'argomento, anche se possono essere di
difficile lettura. Secondo me l'argomento è interessante e merita
approfondimento, vista la criticità del consumo di memoria in applicazioni
server-side.
Luigi.

 riferimento anche a tutti gli oggetti contenuti in esso
Perfetto Luigi, grazie della conferma!
Meno male, pensavo di veder crollata anche questa certezza :-)
A rigor di logica, quindi, se tutte le variabili sono all'interno
della mia classe wbPIPPO, basta che termino di usare la mia classe
nelle pagine JSP, e tutti i rierimenti agli oggetti vengono
rilasciati.
Bene, almeno fino qua ci siamo. Grazie Mille!

Ancora una considerazione:
una delle mie classi, chiamando una determinata funzione, setta 26
variabili di sessione, tutte assieme, in questo modo:
  session.putValue("ALFA", "1");
  session.putValue("BETA", "5");
  ...
Ebbene, queste variabili vengono utilizzate all'interno di tutta la
sessione, e dovrebbero morire non appena si chiude il browser.
Per morire muoiono, ma quello che mi chiedo è se hanno qualche
influenza anche sul servlet container (Tomcat), ossia se vengono
allocate anche lì o se invece sono solo a livello di client (come ho
sempre pensato).
Perché se vengono allocate anche su Tomcat, come diavolo faccio a
disallocarle? Non posso certo settarle a null un attimo prima che il
browser venga chiuso...

 browser venga chiuso...
A questo ci pensa il timeout di sessione.

Ciao Luigi,
grazie per avermi chiarito le idee sulla sessione, ero convinto che le
variabili di sessione venissero memorizzate anche nel client (cioè nel
browser).
Eppure la memoria occupata dal processo "java.exe", ossia da Tomcat,
non cala mai; ho portato il server fino al collasso (finché è apparso
un message box: "your system is running low on virtual memory"), ma il
garbage collector (se è entrato in azione) non ha liberato nulla.
Scrivo qui ciò che succede, ad ogni connessione di un client:
1) Pagina JSP di login: esegue il seguente codice:
   <jsp:useBean  class="Package1.wbLogin"  id="login"  scope="page">
   <%
     login.initialize(pageContext,"Package1_Package1Module.qlogin");
     login.execute();
     login.setReleaseApplicationResources(true);
   %>
   </jsp:useBean>
   Il metodo login.execute() setta 6 variabili, con session.putValue
   (che un giorno o l'altro cambierò in session.setAttribute).
   Sulla console di Tomcat appare il seguente messaggio:
   Loading from CLASSPATH Package1_Package1Module.properties
   *******Package1_Package1Module - Session timeout is:20
   La memoria del processo "java.exe" aumenta di circa 2 MB.
2) Pagina JSP di test login: esegue il seguente codice:
   <%   // assicura che l'applicazione sia registrata
   oracle.jbo.html.jsp.JSPApplicationRegistry.registerApplicationFromPropertyFile(session,
"Package1_Package1Module");
   %>
 
   ...e fa un test su una variabile, decidendo se fare un sendRedirect
   alla pagina di login;
3) Passati i 20 secondi di inattività, arriva il timeout di sessione.
   Sulla console ti Tomcat appare:
   ************* AM was releasedPackage1.Package1ModuleImpl@7e46
Le variabili di sessione a questo punto non esistono più: infatti, se
con la stessa finestra del browser tento di accedere a una pagina JSP
che testa le variabili di sessione, giustamente mi ritornano tutte un
valore null.
Ne deduco che la sessione è eleggibile per il garbage collector, e che
un po' di memoria verrà liberata.
Eppure non capisco quando viene liberata, ho voluto vedere apposta
aprendo 200 clients, fino a portare la memoria del server quasi ad
esaurimento, poi ho chiuso tutti i clients, ho atteso il timeout, ne
ho riaperti altri ma la memoria non è mai calata.
E' vero che non ha mai dato OutOfMemory, ma il server cominciava a
frullare tantissimo e a rallentare spaventosamente.
Mi viene anche il sospetto che venga creata una nuova istanza del
javabean, tramite la tag <jsp:useBean>, anziché presa dal pool.
Eventualmente come si può sapere con certezza se tutto va come deve
andare?
Grazie di tutto, sto apprendendo di più su questo NG che non nei forum
dedicati a JDeveloper e Oracle...

javabean, tramite la tag <jsp:useBean>, anziché presa dal pool.
useBean non garantisce affatto il riuso delle istanze; poi se qualche
servlet container lo implementa in modo che non dia effetti collaterali,
tanto meglio.