У меня немного другое видение ситуации, проекты с небольшими нагрузками не заметят проблемы с кешем, потому что буферы будут успевать освобождаться, и память новая аллоцироваться не будет. А если создают какой-нибудь концепт, то он нужен здесь и сейчас, и большинство предпочтут ByteBuffer.wrap(byte[] data), потому что это проще.
Со средними проектами немного иначе, из них до высоких нагрузок доходят гораздо меньше, чем завершают свой жизненный цикл или остаются в средняках. И в какой-то момент, "jdk.nio.maxCachedBufferSize" может стать настоящим спасением, чтоб проект мог дожить своё время, или дать отсрочку на чтение Вашей статьи.
В методе получения временного буфера написано, что если размер запрашиваемого буфера превышает некий порог, то кеш не используется. Вы не пробовали запретить кеширование буферов, установив "jdk.nio.maxCachedBufferSize" в ноль, интересно, насколько это было бы применимо для средних нагрузок?
public static ByteBuffer getTemporaryDirectBuffer(int size) {
// If a buffer of this size is too large for the cache, there
// should not be a buffer in the cache that is at least as
// large. So we'll just create a new one. Also, we don't have
// to remove the buffer from the cache (as this method does
// below) given that we won't put the new buffer in the cache.
if (isBufferTooLarge(size)) {
return ByteBuffer.allocateDirect(size);
}
....
/**
* Returns the max size allowed for a cached temp buffers, in
* bytes. It defaults to Long.MAX_VALUE. It can be set with the
* jdk.nio.maxCachedBufferSize property. Even though
* ByteBuffer.capacity() returns an int, we're using a long here
* for potential future-proofing.
*/
private static long getMaxCachedBufferSize() {
String s = java.security.AccessController.doPrivileged(
new PrivilegedAction<String>() {
@Override
public String run() {
return System.getProperty("jdk.nio.maxCachedBufferSize");
}
});
В Ваш синглтон всегда инжектится один, и только один экземпляр Bank. Только в случае использования proxyMode=TARGET_CLASS, инжектится не объект класса, а прокси. Вы всегда получаете один и тот же объект, когда вызываете метод синглтона getNewBank(), можете это проверить.
Пример ниже, в котором получены два экземпляра Bank через BankService, убеждаемся, что получили один и тот же экземпляр и вызываем дважды метод getInput() для обоих Bank. Было создано 4 реальных экземпляра Bank, по одному на каждый вызов метод. Остаётся вопрос, действительно ли разработчик хотел бы вызвать метод getInput() на 4 разных объектах, или же только на 2, как это видно в коде.
final BankService bankService = ctx.getBean(BankService.class);
final Bank bank1 = bankService.getBank();
final Bank bank2 = bankService.getBank();
if(Objects.equals(bank1, bank2)) {
System.out.println("There is the same bank");
IntStream.range(0, 2)
.forEach(i -> {
System.out.println(bank1.getInput());
System.out.println(bank2.getInput());
});
}
Результат выполнения
There is the same bank Created new bank. 3dafc23c-ad22-4a05-96af-ec155cc9cd46 Created new bank. e328e94f-3a62-44df-837f-df3c8f6fd440 Created new bank. 39381838-c273-4131-9a54-0e4736cf3155 Created new bank. 3f7dca89-15ef-49e8-b71f-4289790d51e3
В Вашем реализации, в синглтон внедряется не реальный объект класса, а прокси. И при каждом вызове его методов, прокси решает, создавать новый экземпляр класса для вызова или нет. То есть, в Вашем случае, при каждом вызове метода getInput(), прокси решил создавать новый объект. Вызовите метод getInput() на одном и том же прокси 100 раз, думаю, там будет создано гораздо больше экземпляров, чем 1.
Простой пример, получен один бин из Вашей реализации, и на одном и том же бине вызван 500 раз один и тот же метод, вывод по спойлером под кодом.
final Bank bank = ctx.getBean(Bank.class);
IntStream.range(0, 500)
.forEach(i -> System.out.println(bank.getInput()));
Вывод кода выше
new bank created 356f5044-9117-4d2a-b67a-e7d1690cc84b new bank created 53950529-5dfb-4c85-b95f-0e84f25b2108 new bank created bcd131c0-09d8-49e7-ad2b-4331aac0cabf new bank created f8fa5bbe-ee93-42cb-ab16-e153a0a013ed new bank created 5707d254-78e9-4bf5-85d6-4b842da36403 new bank created 648d3719-fb8b-4c39-b3af-ca1e9736a05d new bank created 102b8341-5309-4dfc-9cb3-2d3e3717971d new bank created 9e9e1189-7eb5-43cd-b9c8-b2438c0db6c9 new bank created 3b762b69-2746-4ef4-9a8e-36238129617b new bank created 704b9894-47fa-420b-9c7f-b86e019bcebe new bank created 9d1a519f-d841-4cb4-ace2-2fbcd0cb983d new bank created 0e55e9a6-662e-4fed-b568-dd44abe5829d new bank created 43162b6d-ed89-451a-9fce-cec798862d96 new bank created 427bcda1-6d2d-4cf0-8b1e-17aaaec1ebee
У меня немного другое видение ситуации, проекты с небольшими нагрузками не заметят проблемы с кешем, потому что буферы будут успевать освобождаться, и память новая аллоцироваться не будет. А если создают какой-нибудь концепт, то он нужен здесь и сейчас, и большинство предпочтут ByteBuffer.wrap(byte[] data), потому что это проще.
Со средними проектами немного иначе, из них до высоких нагрузок доходят гораздо меньше, чем завершают свой жизненный цикл или остаются в средняках. И в какой-то момент, "jdk.nio.maxCachedBufferSize" может стать настоящим спасением, чтоб проект мог дожить своё время, или дать отсрочку на чтение Вашей статьи.
Денис, спасибо за отличную статью!
В методе получения временного буфера написано, что если размер запрашиваемого буфера превышает некий порог, то кеш не используется. Вы не пробовали запретить кеширование буферов, установив "jdk.nio.maxCachedBufferSize" в ноль, интересно, насколько это было бы применимо для средних нагрузок?
Видимо, я не смог донести до Вас свою мысль.
В Ваш синглтон всегда инжектится один, и только один экземпляр Bank. Только в случае использования
proxyMode=TARGET_CLASS
, инжектится не объект класса, а прокси. Вы всегда получаете один и тот же объект, когда вызываете метод синглтона getNewBank(), можете это проверить.Пример ниже, в котором получены два экземпляра Bank через BankService, убеждаемся, что получили один и тот же экземпляр и вызываем дважды метод getInput() для обоих Bank. Было создано 4 реальных экземпляра Bank, по одному на каждый вызов метод. Остаётся вопрос, действительно ли разработчик хотел бы вызвать метод getInput() на 4 разных объектах, или же только на 2, как это видно в коде.
Результат выполнения
There is the same bank
Created new bank.
3dafc23c-ad22-4a05-96af-ec155cc9cd46
Created new bank.
e328e94f-3a62-44df-837f-df3c8f6fd440
Created new bank.
39381838-c273-4131-9a54-0e4736cf3155
Created new bank.
3f7dca89-15ef-49e8-b71f-4289790d51e3
В Вашем реализации, в синглтон внедряется не реальный объект класса, а прокси. И при каждом вызове его методов, прокси решает, создавать новый экземпляр класса для вызова или нет. То есть, в Вашем случае, при каждом вызове метода getInput(), прокси решил создавать новый объект. Вызовите метод getInput() на одном и том же прокси 100 раз, думаю, там будет создано гораздо больше экземпляров, чем 1.
Простой пример, получен один бин из Вашей реализации, и на одном и том же бине вызван 500 раз один и тот же метод, вывод по спойлером под кодом.
Вывод кода выше
new bank created
356f5044-9117-4d2a-b67a-e7d1690cc84b
new bank created
53950529-5dfb-4c85-b95f-0e84f25b2108
new bank created
bcd131c0-09d8-49e7-ad2b-4331aac0cabf
new bank created
f8fa5bbe-ee93-42cb-ab16-e153a0a013ed
new bank created
5707d254-78e9-4bf5-85d6-4b842da36403
new bank created
648d3719-fb8b-4c39-b3af-ca1e9736a05d
new bank created
102b8341-5309-4dfc-9cb3-2d3e3717971d
new bank created
9e9e1189-7eb5-43cd-b9c8-b2438c0db6c9
new bank created
3b762b69-2746-4ef4-9a8e-36238129617b
new bank created
704b9894-47fa-420b-9c7f-b86e019bcebe
new bank created
9d1a519f-d841-4cb4-ace2-2fbcd0cb983d
new bank created
0e55e9a6-662e-4fed-b568-dd44abe5829d
new bank created
43162b6d-ed89-451a-9fce-cec798862d96
new bank created
427bcda1-6d2d-4cf0-8b1e-17aaaec1ebee
Всё же, внедрять Prototype лучше так, как описано в документации, например, имплементировать интерфейс ApplicationContextAware и вызывать context.getBean(Prototype.class).https://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch03s04.html#beans-factory-method-injection