11.1 优化集合

大部分编程语言语言库会提供至少两种通用类型的容器

  • 顺序容器:将对象保存在某个特定位置,用索引来表示
  • 关联容器:使用对象本身来确定它在集合中应该保存的位置。

图11-1.png

11.2 针对列表的优化考虑

核心Java中,基本上有两种列表,即ArrayList和LinkedList。

11.5 领域对象

领域对象是表示应用程序非常重要的业务概念的代码。例如电商网站的Order订单,OrderItem订单项和DeliverySchedule(交货时间表)。这些类型之间通常会有一定的关系(一个Order有多个与之关联的OrderItem实例)

  1. public class Order {
  2. private final long id;
  3. private final List<OrderItem> items = new ArrayList<>();
  4. private DeliverySchedule schedule;
  5. public Order(long id) {
  6. this.id = id;
  7. }
  8. public DeliverySchedule getSchedule() {
  9. return schedule;
  10. }
  11. public void setSchedule(DeliverySchedule schedule) {
  12. this.schedule = schedule;
  13. }
  14. public List<OrderItem> getItems() {
  15. return items;
  16. }
  17. public long getId() {
  18. return id;
  19. }
  20. }
  21. public class OrderItem {
  22. private final long id;
  23. private final String description;
  24. private final double price;
  25. public OrderItem(long id, String description, double price) {
  26. this.id = id;
  27. this.description = description;
  28. this.price = price;
  29. }
  30. @Override
  31. public String toString() {
  32. return "OrderItem{" + "id=" + id + ", description=" +
  33. description + ", price=" + price + '}';
  34. }
  35. }
  36. public final class DeliverySchedule {
  37. private final LocalDate deliveryDate;
  38. private final String address;
  39. private final double deliveryCost;
  40. private DeliverySchedule(LocalDate deliveryDate, String address,
  41. double deliveryCost) {
  42. this.deliveryDate = deliveryDate;
  43. this.address = address;
  44. this.deliveryCost = deliveryCost;
  45. }
  46. public static DeliverySchedule of(LocalDate deliveryDate, String address,
  47. double deliveryCost) {
  48. return new DeliverySchedule(deliveryDate, address, deliveryCost);
  49. }
  50. @Override
  51. public String toString() {
  52. return "DeliverySchedule{" + "deliveryDate=" + deliveryDate +
  53. ", address=" + address + ", deliveryCost=" + deliveryCost + '}';
  54. }
  55. }

领域类型之间存在所有权关系。如下图。然而,领域对象图中节点上的大部分数据项最终是简单的数据类型,如字符串、基本类型和LocalDateTime对象
image.png

使用jmap -histo 命令。该命令可以帮助我们快速了解Java堆的状态,同样也能通过VisualVM这样的工具来获得。这些工具有助于诊断领域对象在某些情绪下的内存泄漏。
为了说明原因,需要考虑Java堆的几个基本事实

  • 分配的最常见的数据结构包括字符串、字符数组、字节数组和Java集合类型的实例
  • 在jmap中,与泄漏相对应的数据将表现为大得反常的数据集

也就是说,我们希望不管是按照内存容量还是实例数目,拍在前面的一般是来自核心JDK的数据结构,如果在jmap生成的前30个左右的条目出现了特定于应用程序的领域对象,那就是内存泄漏的迹象。
领域对象发生泄漏的另一个常见行为是对“所有代”的影响。之所以出现这种影响,是因为特定类型的对象在应该回收的时候没有被回收,从而导致它们最终会存活足够长的时间而进入Tenured区,在熬过足够多的手机周期后,它们的代极端可能会表现为各种可能的值。