Sunucu sonlandırıldıktan sonra nesneleri doğru şekilde atma


9

Büyük bir C ++ projesi üzerinde çalışıyorum. Diğer birçok sunucuyu içeren çok geniş bir sistem için basit ve kullanıcı dostu bir arayüz sağlayan bir REST API'si sunan bir sunucudan oluşur. Kod tabanı oldukça büyük ve karmaşıktır ve uygun bir tasarım olmadan zaman içinde gelişmiştir. Görevim yeni özellikleri uygulamak ve eski kodu daha kararlı ve güvenilir hale getirmek için yeniden düzenleme / düzeltme yapmaktır.

Şu anda sunucu, işlem sona erdiğinde hiçbir zaman sonlandırılmayan veya atılmayan bir dizi uzun ömürlü nesne oluşturur. Bu, Valgrind'i kaçak tespiti için neredeyse kullanılamaz hale getirir, çünkü binlerce (şüpheli) meşru kaçağı "tehlikeli" olanlardan ayırt etmek imkansızdır.

Benim fikrim fesih öncesinde tüm nesnelerin atılmasını sağlamaktır, ancak bu teklifi yaptığımda, meslektaşlarım ve patronum, işletim sisteminin bu hafızayı yine de serbest bırakacağına (nesneleri herkesin görebileceğine) işaret etti ve nesneleri bıraktı sunucunun kapanmasını yavaşlatır (şu anda temelde bir çağrıdır std::exit). Ben bir "temiz" kapatma prosedürü olması mutlaka bunu kullanmak gerektiğini ima etmedi. Sabırsız hissedersek her zaman arayabiliriz std::quick_exitya da sadece kill -9süreci.

"Linux cinlerinin ve süreçlerinin çoğu kapanırken bellekte yer açmak zahmetine girmiyor" diye cevapladılar. Bunu görebiliyorum, ancak bellek bozulması, çift serbest bırakmalar ve başlatılmamış değişkenler bulduğumdan, projemizin doğru bellek hata ayıklamasına ihtiyacı olduğu da doğru.

Düşüncelerin nelerdir? Anlamsız bir çaba peşinde miyim? Değilse, meslektaşlarımı ve patronumu nasıl ikna edebilirim? Öyleyse, neden ve bunun yerine ne yapmalıyım?


Performans argümanının yanı sıra (bu makul!) Uzun ömürlü nesneleri izole etmek ve onlar için temizleme kodu eklemek çok mu fazla çaba sarf ediyor?
Doc Brown

Yanıtlar:


7

Sunucu işlemine, tüm belleği serbest bırakacak değer ölçümleri sırasında kullanılabilecek bir anahtar ekleyin. Bu anahtarı test için kullanabilirsiniz. Etki, normal operasyonlar sırasında minimum olacaktır.

1000 nesneyi serbest bırakmak birkaç dakika sürecek uzun bir süreç yaşadık. Sadece çıkmak ve ölmelerine izin vermek çok daha etkili oldu. Ne yazık ki, belirttiğiniz gibi, bu valgrind veya diğer araçlarla gerçek bellek sızıntılarını tespit etmeyi zorlaştırdı.

Bu, normal performansı etkilemezken testlerimiz için iyi bir uzlaşma oldu.


1
+1 Pragmatizm FTW. Ölçümde değer vardır, ancak hızlı kapanmada da değer vardır.
Ross Patterson

2
Komut satırı anahtarına alternatif olarak, #ifdef DEBUG bloğu içindeki kalıcı nesnelerin silinmesini de uygulamayı düşünebilirsiniz.
Jules

3

Burada anahtar şudur:

Bunu görebiliyorum, ancak bellek bozulması, çift serbest bırakmalar ve başlatılmamış değişkenler bulduğumdan, projemizin doğru bellek hata ayıklamasına ihtiyacı olduğu da doğru.

Bu hemen hemen doğrudan kod tabanınızın umut ve dizgiden başka bir şeyden bir araya getirildiğini ima eder. Yetkili C ++ programcılarında çift serbestlik yoktur.

Kesinlikle anlamsız bir uğraş peşindesiniz, olduğu gibi, asıl sorunun küçük bir belirtisini ele alıyorsunuz, yani kodunuz Apollo 13 servis modülü kadar güvenilir.

Sunucunuzu RAII ile doğru bir şekilde programlarsanız, bu sorunlar oluşmaz ve sorunuzdaki sorun giderilir. Ayrıca, kodunuz zaman zaman doğru şekilde çalışabilir. Bu nedenle, açıkça en iyi seçenektir.


Kesinlikle sorun daha büyük resimde yatıyor. Bununla birlikte, bir projeyi daha iyi şekillendirmek için yeniden düzenleme / yeniden yazma için kaynak ve izin bulabilmesi çok nadirdir.
Cengiz Can

@CengizCan: Hataları düzeltmek istiyorsanız, yeniden düzenleme yapmanız gerekir. İşte böyle çalışır.
DeadMG

2

İyi bir yaklaşım, sınıflandırma yoluyla iş arkadaşlarınızla tartışmayı daraltmak olacaktır. Büyük bir kod tabanı göz önüne alındığında, kesinlikle tek bir neden değil, uzun yaşayan nesneler için çok sayıda (tanımlanabilir) neden var.

Örnekler:

  • Kimsenin başvurmadığı uzun yaşayan nesneler (gerçek sızıntılar). Bir programlama mantığı hatasıdır. Bellek önceliğinizin zaman içinde büyümesinden (ve uygulama kalitenizi düşürmesinden) sorumlu OLMADIĞI daha düşük önceliğe sahip olanları düzeltin. Eğer bellek ayak izinizi zamanla büyütürlerse, onları daha yüksek önceliğe göre düzeltin.

  • Hala referans alınan ancak artık kullanılmayan (program mantığı nedeniyle), ancak bellek ayak izinizi büyütmeyen uzun yaşayan nesneler. Kod gözden geçirin ve buna yol açan diğer hataları bulmaya çalışın. Kasıtlı (performans) bir optimizasyonsa kod tabanına yorumlar ekleyin.

  • "Yaşayan" uzun yaşam nesneleri. Örneğin tekli desen. Özellikle çok iş parçacıklı bir uygulama ise, kurtulmak gerçekten zordur.

  • Geri dönüşümlü nesneler. Uzun yaşayan nesnelerin her zaman kötü olması gerekmez. Ayrıca faydalı olabilirler. Yüksek frekanslı bellek ayırmalarına / ayırmalarına sahip olmak yerine, böyle bir bellek bloğuna tekrar ihtiyaç duyulduğunda çizmek için kullanılmayan nesneleri bir kaba eklemek, uygulamanın hızlandırılmasına ve yığın parçalanmasının önlenmesine yardımcı olabilir. Belki de özel bir "enstrümantasyon / kontrol edilmiş" yapıda, kapanma zamanında serbest bırakılmalıdır.

  • "paylaşılan nesneler" - birden fazla başka nesne tarafından kullanılan (referans verilen) nesneler ve hiç kimse onları ne zaman serbest bırakmak için kaydedildiğini tam olarak bilmiyor. Bunları referans sayılan nesnelere dönüştürmeyi düşünün.

Bu serbest bırakılmamış nesnelerin gerçek nedenlerini sınıflandırdıktan sonra, vakaya göre vaka girmek ve bir çözüm bulmak çok daha kolaydır.


0

IMHO, bu nesnelerin yaşamları asla sistem kapatıldığında asla yapılmamalı ve ölmeye bırakılmamalıdır. Hepimizin bildiği küresel değişkenlerin kötü kötü kötü kötü kötü. Özellikle akıllı işaretçiler çağında, bunu tembellik dışında bir neden yoktur. Ama daha da önemlisi, sisteminize birinin bir gün uğraşması gereken bir miktar teknik borç ekler.

"Teknik borç" fikri, böyle bir kısayol aldığınızda, birinin gelecekte kodu değiştirmek istediğinde (diyelim ki, istemcinin "çevrimdışı moda" veya "uyku moduna geçmesini istiyorum" "veya işlemi yeniden başlatmadan sunucuları değiştirebilmek istiyorum), atladığınız şeyi yapmak için çaba sarf etmek zorunda kalacaklar. Ancak kodunuzu koruyacaklar, bu yüzden neredeyse sizin kadar çok şey bilmeyecekler, bu yüzden onları daha uzun sürecek (% 20 daha uzun konuşmuyorum, 20 kat daha uzun konuşuyorum!). Siz olsanız bile, o zaman bu kod üzerinde haftalarca veya aylarca çalışmazsınız ve doğru bir şekilde uygulamak için örümcek ağlarını tozdan almak çok daha uzun sürer.

Bu durumda, sunucu nesnesi ile "uzun ömürlü" nesneler arasında çok sıkı bir bağlantınız var gibi görünüyor ... bir kaydın sunucuyla bağlantıyı geçebileceği (ve yapması gereken) zamanlar vardır. Bir sunucudaki bir nesnede yapılan her değişikliği korumak pahalıya mal olabilir, bu nedenle genellikle ortaya çıkan nesnelerin sunucu nesnesine gerçekten bağlanması daha iyidir, sunucuyu gerçekten değiştiren kaydet ve güncelle aramaları ile. Buna genellikle etkin kayıt düzeni denir. Görmek:

http://en.wikipedia.org/wiki/Active_record_pattern

C ++, ben her etkin kayıt sunucu bağlantısı karanlık giderse akıllıca şeyler atabilir sunucuya bir shor_ptr var olurdu. Bu sınıflar, ihtiyaçlarınıza bağlı olarak tembel veya toplu olarak doldurulabilir, ancak bu nesnelerin ömrü yalnızca kullanıldıkları yerde olmalıdır.

Ayrıca bakınız:

Bir işlemden çıkmadan önce kaynakları boşaltmak zaman kaybı mıdır?

Diğer


This reeks of global variables"Özgürleştirilmesi gereken binlerce nesne var" dan "küresel olmaları" na nasıl gidersiniz? Bu bir mantık sıçraması.
Doval

0

Süresiz olarak hayatta kalması gereken nesnelerin nerede tahsis edildiğini kolayca belirleyebiliyorsanız, bir kez olasılıkla bir valgrind sızıntı raporunda görünmemesi veya sadece tek bir tahsis gibi görünmesi için alternatif bir tahsis mekanizması kullanarak bunları tahsis etmek olacaktır.

Bu fikre aşina değilseniz , c ++'da iktidarsız özel bellek ayırmanın nasıl yapılacağı hakkında bir makale , ancak silmenin hiç ele alınması gerekmediği için çözümünüzün bu makaledeki örneklerden daha basit olabileceğini unutmayın. !

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.