Penggunaan langka WeakReference?
Saya memiliki kelas yang instansinya diinisialisasi dan digunakan oleh flatform yang mendasarinya.
class MyAttributeConverter implements AttributeConverter<XX, YY> {
public YY convertToDatabaseColumn(XX attribute) { return null; }
public XX convertToEntityAttribute(YY dbData) { return null; }
}
Tidak ada yang salah dan saya pikir saya perlu menambahkan beberapa metode statis untuk digunakan sebagai referensi metode.
private static MyAttributeConverter instance;
// just a lazy-initialization;
// no synchronization is required;
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
if (instance == null) {
instance = new MyAttributeConverter();
}
return instance;
}
// do as MyAttributeConverter::toDatabaseColumn(xx)
public static YY toDatabaseColumn(XX attribute) {
return instance().convertToDatabaseColumn(attribute);
}
public static XX toEntityAttribute(YY dbData) {
return instance().convertToEntityAttribute(attribute);
}
Masih tidak ada yang salah (saya percaya) dan saya tidak suka instancebertahan dengan kelas dan itulah mengapa saya mencoba melakukan ini.
private static WeakReference<MyAttributeConverter> reference;
public static <R> R applyInstance(Function<? super MyAttributeConverter, ? extends R> function) {
MyAttributeConverter referent;
if (reference == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
referent = reference.get();
if (referent == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
return function.apply(referent); // @@?
}
Saya pada dasarnya bahkan tidak tahu cara menguji kode ini. Dan saya minta maaf atas pertanyaan saya yang masing-masing mungkin agak kabur.
- Apakah ini pendekatan (benar / salah)?
- Apakah ada kemungkinan
reference.get()di dalamfunction.applyidiom itunull? - Adakah kemungkinan bahwa mungkin ada beberapa masalah seperti kebocoran memori?
- Haruskah saya mengandalkan
SoftReferencedaripadaWeakReference?
Terima kasih.
Jawaban
Perhatikan bahwa metode seperti
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
if (instance == null) {
instance = new MyAttributeConverter();
}
return instance;
}
tidak aman untuk benang, karena mengandung dua bacaan instancelapangan; masing-masing dari mereka mungkin melihat pembaruan yang dibuat oleh utas lain atau tidak. Ini menyiratkan bahwa pembacaan pertama masuk instance == nulldapat melihat nilai yang lebih baru yang ditulis oleh utas lain sedangkan yang kedua return instance;dapat mengevaluasi ke nilai sebelumnya, yaitu null. Jadi metode ini bisa kembali nullketika lebih dari satu thread sedang mengeksekusinya secara bersamaan. Ini adalah kasus sudut yang langka, namun metode ini tidak aman. Anda memerlukan variabel lokal untuk memastikan bahwa pengujian dan pernyataan pengembalian menggunakan nilai yang sama.
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
MyAttributeConverter current = instance;
if (current == null) {
instance = current = new MyAttributeConverter();
}
return current;
}
Ini masih aman hanya jika MyAttributeConvertertidak dapat diubah hanya dengan menggunakan finalbidang. Jika tidak, utas dapat mengembalikan instance yang dibuat oleh utas lain dalam keadaan dibuat tidak lengkap.
Anda dapat menggunakan cara sederhana untuk membuatnya aman tanpa kendala tersebut:
private static final MyAttributeConverter instance = new MyAttributeConverter();
private static MyAttributeConverter instance() {
return instance;
}
Ini masih malas karena inisialisasi kelas hanya terjadi pada salah satu pemicu yang ditentukan , yaitu pemanggilan pertama metode instance().
Penggunaan Anda atas WeakReferencetunduk pada masalah yang sama. Lebih lanjut, tidak jelas mengapa Anda menggunakan pemanggilan rekursif metode Anda pada dua titik di mana Anda sudah memiliki argumen yang diperlukan dalam variabel lokal.
Implementasi yang benar bisa jauh lebih sederhana:
private static WeakReference<MyAttributeConverter> reference;
public static <R> R applyInstance(
Function<? super MyAttributeConverter, ? extends R> function) {
WeakReference<MyAttributeConverter> r = reference;
MyAttributeConverter referent = r != null? r.get(): null;
if (referent == null) {
referent = new MyAttributeConverter();
reference = new WeakReference<>(referent);
}
return function.apply(referent);
}
Tetapi sebelum Anda akan menggunakannya, Anda harus mempertimbangkan kembali apakah kode yang rumit sepadan dengan usaha. Fakta bahwa Anda menerima kebutuhan untuk merekonstruksi objek saat sampah telah dikumpulkan, bahkan berpotensi membuat beberapa instance pada pemanggilan bersamaan, menunjukkan bahwa Anda tahu bahwa konstruksi tersebut akan murah. Jika konstruksinya murah, Anda mungkin tidak perlu meng-cache instance-nya sama sekali.
Pertimbangkan saja
public static <R> R applyInstance(
Function<? super MyAttributeConverter, ? extends R> function) {
return function.apply(new MyAttributeConverter());
}
Setidaknya patut dicoba, mengukur kinerja aplikasi dan membandingkannya dengan pendekatan lain.
Di sisi lain, sepertinya instance tidak menggunakan memori dalam jumlah besar atau menyimpan sumber daya non-memori. Sebaliknya, Anda lebih khawatir tentang kemungkinan beberapa kejadian terbang di sekitar. Jadi varian lain yang patut untuk dicoba dan dibandingkan, adalah yang ditunjukkan di atas menggunakan kolom static finaldengan inisialisasi kelas malas dan tidak ada kesempatan untuk mengumpulkan sampah dari objek kecil itu.
Klarifikasi terakhir. Kamu bertanya
Apakah ada kemungkinan
reference.get()di dalamfunction.applyidiom itunull?
Karena tidak ada reference.get()pemanggilan dalam evaluasi function.apply, tidak ada kemungkinan bahwa pemanggilan seperti itu dapat dievaluasi ke nullpada saat ini. Fungsi tersebut menerima referensi yang kuat dan karena kode pemanggil memastikan bahwa referensi yang kuat ini bukan null, fungsi tersebut tidak akan pernah menjadi nullselama pemanggilan applymetode.
Umumnya, pengumpul sampah tidak akan pernah mengubah status aplikasi sedemikian rupa sehingga kode yang menggunakan referensi yang kuat akan melihat perbedaan (membiarkan ketersediaan lebih banyak memori dikesampingkan).
Namun karena Anda menanyakannya secara khusus reference.get(), pengumpul sampah dapat mengumpulkan objek setelah penggunaan terakhirnya , terlepas dari eksekusi metode atau cakupan lokalnya . Jadi referensi bisa dikumpulkan selama eksekusi applymetode saat metode ini tidak menggunakan objek lagi. Pengoptimalan waktu proses memungkinkan hal ini terjadi lebih awal dari yang Anda duga dengan melihat kode sumber , karena apa yang terlihat seperti penggunaan objek (misalnya pembacaan bidang) mungkin tidak menggunakan objek pada waktu proses (misalnya karena nilai tersebut sudah ada di Register CPU, menghilangkan kebutuhan untuk mengakses memori objek). Seperti yang dikatakan, semua tanpa mengubah perilaku metode.
Jadi hipotesis reference.get()selama pelaksanaan applymetode pada prinsipnya dapat mengevaluasi null, tetapi tidak ada alasan untuk khawatir, seperti yang dikatakan, perilaku applymetode tidak berubah. JVM akan menyimpan memori objek selama diperlukan untuk memastikan eksekusi metode yang benar ini.
Tapi penjelasan itu hanya untuk kelengkapan. Seperti yang dikatakan, Anda tidak boleh menggunakan referensi lemah atau lunak untuk objek yang tidak memiliki resource mahal.