java—会话对象在我的应用程序中占用资源吗?垃圾收集器不会移除它们吗?

yh2wf1be  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(296)

我在headfirstjsp和servlets中遇到了partpage:241 where 它说我们必须摆脱会话,如图所示:

后来他们提出了方法 invalidate() 以及 setMaxInactiveInterval() 用于减少服务器中过时会话的数量。看完之后,我有点困惑。
首先,我使用 HttpSession s = request.getSession() 然后做一些手术。知道一个请求将为该servlet生成一个线程,意味着该变量 s 将只对给定线程有作用域。一旦线程完成,变量 s 不会存在。这进一步意味着堆中的会话对象将没有来自的活动引用 s =垃圾收集。
所以如果没有新的请求,就不应该有任何会话对象占用我的资源,对吗?那为什么这本书告诉我必须除掉他们?垃圾收集器不应该单独完成它的工作吗?
有人能纠正我的错误吗?会话对象真的存储在堆中吗?因为我想不出他们会在哪里。

ibps3vxo

ibps3vxo1#

这里有好多东西要拆开,我们一件一件地拿吧。

会话和Cookie

http是一种无状态协议。这意味着,对于服务器,每个http请求都被视为独立于其他http请求。因此,如果您向同一台服务器发出多个请求,服务器实际上并不关心它们是否来自同一个客户机:接收一个请求并生成一个响应,接收另一个请求并生成另一个响应,依此类推。
但是,在有些情况下,您需要将来自同一个用户的一堆请求标识为与服务器的较长时间交互,而不仅仅是孤立的请求。这就是会话和饼干的用武之地。
会话标识同一用户与服务器的多个交互,并允许您维护用户标识和一些有用的数据,这些数据的生命周期可以跨越所有请求。这意味着会话是有状态的,而不是无状态的。
会话基本上是服务器保存在内存中的对象,它充当在请求之间保存任何数据的容器。这个对象也可以保存在磁盘上或数据库中(例如,当您重新启动服务器并且不想丢失活动会话时),但是为了简单起见,只需将它视为内存中的对象。是的,它存储在堆中。
因此,如果应用程序需要在请求之间存储状态,可以在服务器上创建会话对象。但是,如何将属于某个会话的请求与不属于该会话的其他请求区分开来呢?答案是饼干。
当用户发出第一个请求时,服务器可以创建一个会话,并将添加到响应中的会话标识返回给您。然后,当用户发出另一个请求时,会话标识被发送回服务器,现在这个请求被标识为更大交互的一部分,会话的一部分。哪个环节?因此会话是存储在服务器上的对象,任何作为会话交互一部分的请求都需要用会话标识。

垃圾收集会话

由于session对象是位于堆上的java对象,因此可以对其进行垃圾收集。然而,事情并不是那么简单。
例如,比较以下几段代码。这是:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // ...
    Object s = new Object();
    // ...
}

有了这个:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // ...
    HttpSession s = request.getSession();
    // ...
}

在第一个示例中,您创建了一个存储在堆上的对象。一旦 doGet 方法结束时,此对象有资格进行垃圾回收,因为除了 s 方法返回时超出范围。
这里的关键部分是“不再引用”。当无法再从jvm中存在的任何活动引用访问对象时,对象就有资格进行垃圾回收。当 doGet 方法结束, s 没有了,所以没有任何东西指向您创建的对象。用一个 HttpSession 事情不同了。
在第二段代码中,您没有创建会话对象,而是请求服务器“给您”一个会话对象。假设服务器保留了一个Map,其中包含会话对象作为值,会话id是访问它们的键。当您请求服务器为您提供与 HttpSession s = request.getSession() 它所做的是从请求中查看session\ id cookie,以查找与该请求相关联的会话,并为您提供对session对象的引用。现在您有两个对session对象的引用,一个由服务器保存在会话Map中,另一个存储在会话Map中 s . 当 doGet 方法结束时 s 引用已消失,但服务器仍保留对会话对象的引用(因为它需要它来处理可能作为更大交互的一部分到达的进一步请求)。在这种情况下,会话对象不符合垃圾收集的条件,因为它可以通过jvm中的活动引用(服务器持有的引用)访问。
因此,如果您不清除会话,服务器就无法知道该会话是有用的还是无用的,因为它不知道以后是否会有另一个请求请求它。因此会话对象永远留在服务器中。由于服务器可以运行数月或数年而不需要重新启动或关闭,会话对象可以累积并消耗所有内存。垃圾收集器不会删除它们,因为服务器持有对它们的引用。最终会出现outofmemory错误,服务器崩溃。

会话超时

当然,您不希望服务器崩溃。因此,您需要使会话无效,并告诉服务器“嘿,我不再需要那个会话对象了。你可以摆脱它”。在这种情况下,服务器会将其从Map中移除,并且在没有任何活动引用的情况下,现在可以对其进行垃圾收集。
但由于所有这些交互都是通过网络与http进行的,就像书中提到的例子一样,浏览器可能会崩溃,用户的计算机可能会崩溃,用户只需离开即可。因此,您可能没有机会使会话无效并告诉服务器可以处理它,因此它将永远留在那里。
这就是会话超时的原因。在构建应用程序时,还可以配置一个会话超时来告诉服务器“嘿,如果这个会话上有x分钟的不活动,你可以把它去掉”。所以现在,如果客户机在没有使会话失效的情况下离开,服务器可以有一个故障保护机制来清除过期的会话,这样它们就不会永远留在内存中。

p5cysglq

p5cysglq2#

这个问题及其公认的答案很好地说明了http会话机制的目的。
如果要在请求之间保持状态,则需要将该状态存储在某个位置。我相信大多数应用服务器在默认情况下都会使用堆。您可以将其视为以用户会话id为键的Map,以及包含存储数据的对象作为其值。
除非您采取措施从Map中删除对象,否则该Map将永远增长,方法是在用户注销时使会话对象无效,或者具有某种非活动限制,允许应用程序服务器在设置的时间段后自行清理旧条目。

相关问题